NPC_move.cpp 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845
  1. //
  2. // NPC_move.cpp
  3. //
  4. // leave this line at the top for all NPC_xxxx.cpp files...
  5. #include "g_headers.h"
  6. #include "b_local.h"
  7. #include "g_nav.h"
  8. #include "anims.h"
  9. extern qboolean NPC_ClearPathToGoal( vec3_t dir, gentity_t *goal );
  10. extern qboolean NAV_MoveDirSafe( gentity_t *self, usercmd_t *cmd, float distScale = 1.0f );
  11. void CG_Cylinder( vec3_t start, vec3_t end, float radius, vec3_t color );
  12. qboolean G_BoundsOverlap(const vec3_t mins1, const vec3_t maxs1, const vec3_t mins2, const vec3_t maxs2);
  13. extern int GetTime ( int lastTime );
  14. navInfo_t frameNavInfo;
  15. extern qboolean FlyingCreature( gentity_t *ent );
  16. extern qboolean PM_InKnockDown( playerState_t *ps );
  17. extern cvar_t *g_navSafetyChecks;
  18. extern qboolean Boba_Flying( gentity_t *self );
  19. extern qboolean PM_InRoll( playerState_t *ps );
  20. #define APEX_HEIGHT 200.0f
  21. #define PARA_WIDTH (sqrt(APEX_HEIGHT)+sqrt(APEX_HEIGHT))
  22. #define JUMP_SPEED 200.0f
  23. static qboolean NPC_TryJump();
  24. static qboolean NPC_Jump( vec3_t dest, int goalEntNum )
  25. {//FIXME: if land on enemy, knock him down & jump off again
  26. float targetDist, travelTime, impactDist, bestImpactDist = Q3_INFINITE;//fireSpeed,
  27. float originalShotSpeed, shotSpeed, speedStep = 50.0f, minShotSpeed = 30.0f, maxShotSpeed = 500.0f;
  28. qboolean belowBlocked = qfalse, aboveBlocked = qfalse;
  29. vec3_t targetDir, shotVel, failCase;
  30. trace_t trace;
  31. trajectory_t tr;
  32. qboolean blocked;
  33. int elapsedTime, timeStep = 250, hitCount = 0, aboveTries = 0, belowTries = 0, maxHits = 10;
  34. vec3_t lastPos, testPos, bottom;
  35. VectorSubtract( dest, NPC->currentOrigin, targetDir );
  36. targetDist = VectorNormalize( targetDir );
  37. //make our shotSpeed reliant on the distance
  38. originalShotSpeed = targetDist;//DistanceHorizontal( dest, NPC->currentOrigin )/2.0f;
  39. if ( originalShotSpeed > maxShotSpeed )
  40. {
  41. originalShotSpeed = maxShotSpeed;
  42. }
  43. else if ( originalShotSpeed < minShotSpeed )
  44. {
  45. originalShotSpeed = minShotSpeed;
  46. }
  47. shotSpeed = originalShotSpeed;
  48. while ( hitCount < maxHits )
  49. {
  50. VectorScale( targetDir, shotSpeed, shotVel );
  51. travelTime = targetDist/shotSpeed;
  52. shotVel[2] += travelTime * 0.5 * NPC->client->ps.gravity;
  53. if ( !hitCount )
  54. {//save the first one as the worst case scenario
  55. VectorCopy( shotVel, failCase );
  56. }
  57. if ( 1 )//tracePath )
  58. {//do a rough trace of the path
  59. blocked = qfalse;
  60. VectorCopy( NPC->currentOrigin, tr.trBase );
  61. VectorCopy( shotVel, tr.trDelta );
  62. tr.trType = TR_GRAVITY;
  63. tr.trTime = level.time;
  64. travelTime *= 1000.0f;
  65. VectorCopy( NPC->currentOrigin, lastPos );
  66. //This may be kind of wasteful, especially on long throws... use larger steps? Divide the travelTime into a certain hard number of slices? Trace just to apex and down?
  67. for ( elapsedTime = timeStep; elapsedTime < floor(travelTime)+timeStep; elapsedTime += timeStep )
  68. {
  69. if ( (float)elapsedTime > travelTime )
  70. {//cap it
  71. elapsedTime = floor( travelTime );
  72. }
  73. EvaluateTrajectory( &tr, level.time + elapsedTime, testPos );
  74. //FUCK IT, always check for do not enter...
  75. gi.trace( &trace, lastPos, NPC->mins, NPC->maxs, testPos, NPC->s.number, NPC->clipmask|CONTENTS_BOTCLIP );
  76. /*
  77. if ( testPos[2] < lastPos[2]
  78. && elapsedTime < floor( travelTime ) )
  79. {//going down, haven't reached end, ignore botclip
  80. gi.trace( &trace, lastPos, NPC->mins, NPC->maxs, testPos, NPC->s.number, NPC->clipmask );
  81. }
  82. else
  83. {//going up, check for botclip
  84. gi.trace( &trace, lastPos, NPC->mins, NPC->maxs, testPos, NPC->s.number, NPC->clipmask|CONTENTS_BOTCLIP );
  85. }
  86. */
  87. if ( trace.allsolid || trace.startsolid )
  88. {//started in solid
  89. if ( NAVDEBUG_showCollision )
  90. {
  91. CG_DrawEdge( lastPos, trace.endpos, EDGE_RED_TWOSECOND );
  92. }
  93. return qfalse;//you're hosed, dude
  94. }
  95. if ( trace.fraction < 1.0f )
  96. {//hit something
  97. if ( NAVDEBUG_showCollision )
  98. {
  99. CG_DrawEdge( lastPos, trace.endpos, EDGE_RED_TWOSECOND ); // TryJump
  100. }
  101. if ( trace.entityNum == goalEntNum )
  102. {//hit the enemy, that's bad!
  103. blocked = qtrue;
  104. /*
  105. if ( g_entities[goalEntNum].client && g_entities[goalEntNum].client->ps.groundEntityNum == ENTITYNUM_NONE )
  106. {//bah, would collide in mid-air, no good
  107. blocked = qtrue;
  108. }
  109. else
  110. {//he's on the ground, good enough, I guess
  111. //Hmm, don't want to land on him, though...?
  112. }
  113. */
  114. break;
  115. }
  116. else
  117. {
  118. if ( trace.contents & CONTENTS_BOTCLIP )
  119. {//hit a do-not-enter brush
  120. blocked = qtrue;
  121. break;
  122. }
  123. if ( trace.plane.normal[2] > 0.7 && DistanceSquared( trace.endpos, dest ) < 4096 )//hit within 64 of desired location, should be okay
  124. {//close enough!
  125. break;
  126. }
  127. else
  128. {//FIXME: maybe find the extents of this brush and go above or below it on next try somehow?
  129. impactDist = DistanceSquared( trace.endpos, dest );
  130. if ( impactDist < bestImpactDist )
  131. {
  132. bestImpactDist = impactDist;
  133. VectorCopy( shotVel, failCase );
  134. }
  135. blocked = qtrue;
  136. break;
  137. }
  138. }
  139. }
  140. else
  141. {
  142. if ( NAVDEBUG_showCollision )
  143. {
  144. CG_DrawEdge( lastPos, testPos, EDGE_WHITE_TWOSECOND ); // TryJump
  145. }
  146. }
  147. if ( elapsedTime == floor( travelTime ) )
  148. {//reached end, all clear
  149. if ( trace.fraction >= 1.0f )
  150. {//hmm, make sure we'll land on the ground...
  151. //FIXME: do we care how far below ourselves or our dest we'll land?
  152. VectorCopy( trace.endpos, bottom );
  153. bottom[2] -= 128;
  154. gi.trace( &trace, trace.endpos, NPC->mins, NPC->maxs, bottom, NPC->s.number, NPC->clipmask );
  155. if ( trace.fraction >= 1.0f )
  156. {//would fall too far
  157. blocked = qtrue;
  158. }
  159. }
  160. break;
  161. }
  162. else
  163. {
  164. //all clear, try next slice
  165. VectorCopy( testPos, lastPos );
  166. }
  167. }
  168. if ( blocked )
  169. {//hit something, adjust speed (which will change arc)
  170. hitCount++;
  171. //alternate back and forth between trying an arc slightly above or below the ideal
  172. if ( (hitCount%2) && !belowBlocked )
  173. {//odd
  174. belowTries++;
  175. shotSpeed = originalShotSpeed - (belowTries*speedStep);
  176. }
  177. else if ( !aboveBlocked )
  178. {//even
  179. aboveTries++;
  180. shotSpeed = originalShotSpeed + (aboveTries*speedStep);
  181. }
  182. else
  183. {//can't go any higher or lower
  184. hitCount = maxHits;
  185. break;
  186. }
  187. if ( shotSpeed > maxShotSpeed )
  188. {
  189. shotSpeed = maxShotSpeed;
  190. aboveBlocked = qtrue;
  191. }
  192. else if ( shotSpeed < minShotSpeed )
  193. {
  194. shotSpeed = minShotSpeed;
  195. belowBlocked = qtrue;
  196. }
  197. }
  198. else
  199. {//made it!
  200. break;
  201. }
  202. }
  203. else
  204. {//no need to check the path, go with first calc
  205. break;
  206. }
  207. }
  208. if ( hitCount >= maxHits )
  209. {//NOTE: worst case scenario, use the one that impacted closest to the target (or just use the first try...?)
  210. return qfalse;
  211. //NOTE: or try failcase?
  212. //VectorCopy( failCase, NPC->client->ps.velocity );
  213. //return qtrue;
  214. }
  215. VectorCopy( shotVel, NPC->client->ps.velocity );
  216. return qtrue;
  217. }
  218. #define NPC_JUMP_PREP_BACKUP_DIST 34.0f
  219. trace_t mJumpTrace;
  220. qboolean NPC_CanTryJump()
  221. {
  222. if (!(NPCInfo->scriptFlags&SCF_NAV_CAN_JUMP) || // Can't Jump
  223. (NPCInfo->scriptFlags&SCF_NO_ACROBATICS) || // If Can't Jump At All
  224. (level.time<NPCInfo->jumpBackupTime) || // If Backing Up, Don't Try The Jump Again
  225. (level.time<NPCInfo->jumpNextCheckTime) || // Don't Even Try To Jump Again For This Amount Of Time
  226. (NPCInfo->jumpTime) || // Don't Jump If Already Going
  227. (PM_InKnockDown(&NPC->client->ps)) || // Don't Jump If In Knockdown
  228. (PM_InRoll(&NPC->client->ps)) || // ... Or Roll
  229. (NPC->client->ps.groundEntityNum==ENTITYNUM_NONE) // ... Or In The Air
  230. )
  231. {
  232. return qfalse;
  233. }
  234. return qtrue;
  235. }
  236. qboolean NPC_TryJump(const vec3_t& pos, float max_xy_dist, float max_z_diff)
  237. {
  238. if (NPC_CanTryJump())
  239. {
  240. NPCInfo->jumpNextCheckTime = level.time + Q_irand(1000, 2000);
  241. VectorCopy(pos, NPCInfo->jumpDest);
  242. // Can't Try To Jump At A Point In The Air
  243. //-----------------------------------------
  244. {
  245. vec3_t groundTest;
  246. VectorCopy(pos, groundTest);
  247. groundTest[2] += (NPC->mins[2]*3);
  248. gi.trace(&mJumpTrace, NPCInfo->jumpDest, vec3_origin, vec3_origin, groundTest, NPC->s.number, NPC->clipmask );
  249. if (mJumpTrace.fraction >= 1.0f)
  250. {
  251. return qfalse; //no ground = no jump
  252. }
  253. }
  254. NPCInfo->jumpTarget = 0;
  255. NPCInfo->jumpMaxXYDist = (max_xy_dist)?(max_xy_dist):((NPC->client->NPC_class==CLASS_ROCKETTROOPER)?1200:750);
  256. NPCInfo->jumpMazZDist = (max_z_diff)?(max_z_diff):((NPC->client->NPC_class==CLASS_ROCKETTROOPER)?-1000:-450);
  257. NPCInfo->jumpTime = 0;
  258. NPCInfo->jumpBackupTime = 0;
  259. return NPC_TryJump();
  260. }
  261. return qfalse;
  262. }
  263. qboolean NPC_TryJump(gentity_t *goal, float max_xy_dist, float max_z_diff)
  264. {
  265. if (NPC_CanTryJump())
  266. {
  267. NPCInfo->jumpNextCheckTime = level.time + Q_irand(1000, 3000);
  268. // Can't Jump At Targets In The Air
  269. //---------------------------------
  270. if (goal->client && goal->client->ps.groundEntityNum==ENTITYNUM_NONE)
  271. {
  272. return qfalse;
  273. }
  274. VectorCopy(goal->currentOrigin, NPCInfo->jumpDest);
  275. NPCInfo->jumpTarget = goal;
  276. NPCInfo->jumpMaxXYDist = (max_xy_dist)?(max_xy_dist):((NPC->client->NPC_class==CLASS_ROCKETTROOPER)?1200:750);
  277. NPCInfo->jumpMazZDist = (max_z_diff)?(max_z_diff):((NPC->client->NPC_class==CLASS_ROCKETTROOPER)?-1000:-400);
  278. NPCInfo->jumpTime = 0;
  279. NPCInfo->jumpBackupTime = 0;
  280. return NPC_TryJump();
  281. }
  282. return qfalse;
  283. }
  284. void NPC_JumpAnimation()
  285. {
  286. int jumpAnim = BOTH_JUMP1;
  287. if ( NPC->client->NPC_class == CLASS_BOBAFETT
  288. || (NPC->client->NPC_class == CLASS_REBORN && NPC->s.weapon != WP_SABER)
  289. || NPC->client->NPC_class == CLASS_ROCKETTROOPER
  290. ||( NPCInfo->rank != RANK_CREWMAN && NPCInfo->rank <= RANK_LT_JG ) )
  291. {//can't do acrobatics
  292. jumpAnim = BOTH_FORCEJUMP1;
  293. }
  294. else if (NPC->client->NPC_class != CLASS_HOWLER)
  295. {
  296. if ( NPC->client->NPC_class == CLASS_ALORA && Q_irand( 0, 3 ) )
  297. {
  298. jumpAnim = Q_irand( BOTH_ALORA_FLIP_1, BOTH_ALORA_FLIP_3 );
  299. }
  300. else
  301. {
  302. jumpAnim = BOTH_FLIP_F;
  303. }
  304. }
  305. NPC_SetAnim( NPC, SETANIM_BOTH, jumpAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
  306. }
  307. extern void JET_FlyStart(gentity_t* actor);
  308. void NPC_JumpSound()
  309. {
  310. if ( NPC->client->NPC_class == CLASS_HOWLER )
  311. {
  312. //FIXME: can I delay the actual jump so that it matches the anim...?
  313. }
  314. else if ( NPC->client->NPC_class == CLASS_BOBAFETT
  315. || NPC->client->NPC_class == CLASS_ROCKETTROOPER )
  316. {
  317. // does this really need to be here?
  318. JET_FlyStart(NPC);
  319. }
  320. else
  321. {
  322. G_SoundOnEnt( NPC, CHAN_BODY, "sound/weapons/force/jump.wav" );
  323. }
  324. }
  325. qboolean NPC_TryJump()
  326. {
  327. vec3_t targetDirection;
  328. float targetDistanceXY;
  329. float targetDistanceZ;
  330. // Get The Direction And Distances To The Target
  331. //-----------------------------------------------
  332. VectorSubtract(NPCInfo->jumpDest, NPC->currentOrigin, targetDirection);
  333. targetDirection[2] = 0.0f;
  334. targetDistanceXY = VectorNormalize(targetDirection);
  335. targetDistanceZ = NPCInfo->jumpDest[2] - NPC->currentOrigin[2];
  336. if ((targetDistanceXY>NPCInfo->jumpMaxXYDist) ||
  337. (targetDistanceZ<NPCInfo->jumpMazZDist))
  338. {
  339. return qfalse;
  340. }
  341. // Test To See If There Is A Wall Directly In Front Of Actor, If So, Backup Some
  342. //-------------------------------------------------------------------------------
  343. if (TIMER_Done(NPC, "jumpBackupDebounce"))
  344. {
  345. vec3_t actorProjectedTowardTarget;
  346. VectorMA(NPC->currentOrigin, NPC_JUMP_PREP_BACKUP_DIST, targetDirection, actorProjectedTowardTarget);
  347. gi.trace(&mJumpTrace, NPC->currentOrigin, vec3_origin, vec3_origin, actorProjectedTowardTarget, NPC->s.number, NPC->clipmask);
  348. if ((mJumpTrace.fraction < 1.0f) ||
  349. (mJumpTrace.allsolid) ||
  350. (mJumpTrace.startsolid))
  351. {
  352. if (NAVDEBUG_showCollision)
  353. {
  354. CG_DrawEdge(NPC->currentOrigin, actorProjectedTowardTarget, EDGE_RED_TWOSECOND); // TryJump
  355. }
  356. // TODO: We may want to test to see if it is safe to back up here?
  357. NPCInfo->jumpBackupTime = level.time + 1000;
  358. TIMER_Set(NPC, "jumpBackupDebounce", 5000);
  359. return qtrue;
  360. }
  361. }
  362. // bool Wounded = (NPC->health < 150);
  363. // bool OnLowerLedge = ((targetDistanceZ<-80.0f) && (targetDistanceZ>-200.0f));
  364. // bool WithinNormalJumpRange = ((targetDistanceZ<32.0f) && (targetDistanceXY<200.0f));
  365. bool WithinForceJumpRange = ((fabsf(targetDistanceZ)>0) || (targetDistanceXY>128));
  366. /* if (Wounded && OnLowerLedge)
  367. {
  368. ucmd.forwardmove = 127;
  369. VectorClear(NPC->client->ps.moveDir);
  370. TIMER_Set(NPC, "duck", -level.time);
  371. return qtrue;
  372. }
  373. if (WithinNormalJumpRange)
  374. {
  375. ucmd.upmove = 127;
  376. ucmd.forwardmove = 127;
  377. VectorClear(NPC->client->ps.moveDir);
  378. TIMER_Set(NPC, "duck", -level.time);
  379. return qtrue;
  380. }
  381. */
  382. if (!WithinForceJumpRange)
  383. {
  384. return qfalse;
  385. }
  386. // If There Is Any Chance That This Jump Will Land On An Enemy, Try 8 Different Traces Around The Target
  387. //-------------------------------------------------------------------------------------------------------
  388. if (NPCInfo->jumpTarget)
  389. {
  390. float minSafeRadius = (NPC->maxs[0]*1.5f) + (NPCInfo->jumpTarget->maxs[0]*1.5f);
  391. float minSafeRadiusSq = (minSafeRadius * minSafeRadius);
  392. if (DistanceSquared(NPCInfo->jumpDest, NPCInfo->jumpTarget->currentOrigin)<minSafeRadiusSq)
  393. {
  394. vec3_t startPos;
  395. vec3_t floorPos;
  396. VectorCopy(NPCInfo->jumpDest, startPos);
  397. floorPos[2] = NPCInfo->jumpDest[2] + (NPC->mins[2]-32);
  398. for (int sideTryCount=0; sideTryCount<8; sideTryCount++)
  399. {
  400. NPCInfo->jumpSide++;
  401. if ( NPCInfo->jumpSide > 7 )
  402. {
  403. NPCInfo->jumpSide = 0;
  404. }
  405. switch ( NPCInfo->jumpSide )
  406. {
  407. case 0:
  408. NPCInfo->jumpDest[0] = startPos[0] + minSafeRadius;
  409. NPCInfo->jumpDest[1] = startPos[1];
  410. break;
  411. case 1:
  412. NPCInfo->jumpDest[0] = startPos[0] + minSafeRadius;
  413. NPCInfo->jumpDest[1] = startPos[1] + minSafeRadius;
  414. break;
  415. case 2:
  416. NPCInfo->jumpDest[0] = startPos[0];
  417. NPCInfo->jumpDest[1] = startPos[1] + minSafeRadius;
  418. break;
  419. case 3:
  420. NPCInfo->jumpDest[0] = startPos[0] - minSafeRadius;
  421. NPCInfo->jumpDest[1] = startPos[1] + minSafeRadius;
  422. break;
  423. case 4:
  424. NPCInfo->jumpDest[0] = startPos[0] - minSafeRadius;
  425. NPCInfo->jumpDest[1] = startPos[1];
  426. break;
  427. case 5:
  428. NPCInfo->jumpDest[0] = startPos[0] - minSafeRadius;
  429. NPCInfo->jumpDest[1] = startPos[1] - minSafeRadius;
  430. break;
  431. case 6:
  432. NPCInfo->jumpDest[0] = startPos[0];
  433. NPCInfo->jumpDest[1] = startPos[1] - minSafeRadius;
  434. break;
  435. case 7:
  436. NPCInfo->jumpDest[0] = startPos[0] + minSafeRadius;
  437. NPCInfo->jumpDest[1] = startPos[1] -=minSafeRadius;
  438. break;
  439. }
  440. floorPos[0] = NPCInfo->jumpDest[0];
  441. floorPos[1] = NPCInfo->jumpDest[1];
  442. gi.trace(&mJumpTrace, NPCInfo->jumpDest, NPC->mins, NPC->maxs, floorPos, (NPCInfo->jumpTarget)?(NPCInfo->jumpTarget->s.number):(NPC->s.number), (NPC->clipmask|CONTENTS_BOTCLIP));
  443. if ((mJumpTrace.fraction<1.0f) &&
  444. (!mJumpTrace.allsolid) &&
  445. (!mJumpTrace.startsolid))
  446. {
  447. break;
  448. }
  449. if ( NAVDEBUG_showCollision )
  450. {
  451. CG_DrawEdge( NPCInfo->jumpDest, floorPos, EDGE_RED_TWOSECOND );
  452. }
  453. }
  454. // If All Traces Failed, Just Try Going Right Back At The Target Location
  455. //------------------------------------------------------------------------
  456. if ((mJumpTrace.fraction>=1.0f) ||
  457. (mJumpTrace.allsolid) ||
  458. (mJumpTrace.startsolid))
  459. {
  460. VectorCopy(startPos, NPCInfo->jumpDest);
  461. }
  462. }
  463. }
  464. // Now, Actually Try The Jump To The Dest Target
  465. //-----------------------------------------------
  466. if (NPC_Jump(NPCInfo->jumpDest, (NPCInfo->jumpTarget)?(NPCInfo->jumpTarget->s.number):(NPC->s.number)))
  467. {
  468. // We Made IT!
  469. //-------------
  470. NPC_JumpAnimation();
  471. NPC_JumpSound();
  472. NPC->client->ps.forceJumpZStart = NPC->currentOrigin[2];
  473. NPC->client->ps.pm_flags |= PMF_JUMPING;
  474. NPC->client->ps.weaponTime = NPC->client->ps.torsoAnimTimer;
  475. NPC->client->ps.forcePowersActive |= ( 1 << FP_LEVITATION );
  476. ucmd.forwardmove = 0;
  477. NPCInfo->jumpTime = 1;
  478. VectorClear(NPC->client->ps.moveDir);
  479. TIMER_Set(NPC, "duck", -level.time);
  480. return qtrue;
  481. }
  482. return qfalse;
  483. }
  484. qboolean NPC_Jumping()
  485. {
  486. if ( NPCInfo->jumpTime )
  487. {
  488. if ( !(NPC->client->ps.pm_flags & PMF_JUMPING )//forceJumpZStart )
  489. && !(NPC->client->ps.pm_flags&PMF_TRIGGER_PUSHED))
  490. {//landed
  491. NPCInfo->jumpTime = 0;
  492. }
  493. else
  494. {
  495. // if (NPCInfo->jumpTarget)
  496. // {
  497. // NPC_FaceEntity(NPCInfo->jumpTarget, qtrue);
  498. // }
  499. // else
  500. {
  501. NPC_FacePosition(NPCInfo->jumpDest, qtrue);
  502. }
  503. return qtrue;
  504. }
  505. }
  506. return qfalse;
  507. }
  508. qboolean NPC_JumpBackingUp()
  509. {
  510. if (NPCInfo->jumpBackupTime)
  511. {
  512. if (level.time<NPCInfo->jumpBackupTime)
  513. {
  514. STEER::Activate(NPC);
  515. STEER::Flee(NPC, NPCInfo->jumpDest);
  516. STEER::DeActivate(NPC, &ucmd);
  517. NPC_FacePosition(NPCInfo->jumpDest, qtrue);
  518. NPC_UpdateAngles( qfalse, qtrue );
  519. return qtrue;
  520. }
  521. NPCInfo->jumpBackupTime = 0;
  522. return NPC_TryJump();
  523. }
  524. return false;
  525. }
  526. /*
  527. -------------------------
  528. NPC_CheckCombatMove
  529. -------------------------
  530. */
  531. inline qboolean NPC_CheckCombatMove( void )
  532. {
  533. //return NPCInfo->combatMove;
  534. if ( ( NPCInfo->goalEntity && NPC->enemy && NPCInfo->goalEntity == NPC->enemy ) || ( NPCInfo->combatMove ) )
  535. {
  536. return qtrue;
  537. }
  538. if ( NPCInfo->goalEntity && NPCInfo->watchTarget )
  539. {
  540. if ( NPCInfo->goalEntity != NPCInfo->watchTarget )
  541. {
  542. return qtrue;
  543. }
  544. }
  545. return qfalse;
  546. }
  547. /*
  548. -------------------------
  549. NPC_LadderMove
  550. -------------------------
  551. */
  552. static void NPC_LadderMove( vec3_t dir )
  553. {
  554. //FIXME: this doesn't guarantee we're facing ladder
  555. //ALSO: Need to be able to get off at top
  556. //ALSO: Need to play an anim
  557. //ALSO: Need transitionary anims?
  558. if ( ( dir[2] > 0 ) || ( dir[2] < 0 && NPC->client->ps.groundEntityNum == ENTITYNUM_NONE ) )
  559. {
  560. //Set our movement direction
  561. ucmd.upmove = (dir[2] > 0) ? 127 : -127;
  562. //Don't move around on XY
  563. ucmd.forwardmove = ucmd.rightmove = 0;
  564. }
  565. }
  566. /*
  567. -------------------------
  568. NPC_GetMoveInformation
  569. -------------------------
  570. */
  571. inline qboolean NPC_GetMoveInformation( vec3_t dir, float *distance )
  572. {
  573. //NOTENOTE: Use path stacks!
  574. //Make sure we have somewhere to go
  575. if ( NPCInfo->goalEntity == NULL )
  576. return qfalse;
  577. //Get our move info
  578. VectorSubtract( NPCInfo->goalEntity->currentOrigin, NPC->currentOrigin, dir );
  579. *distance = VectorNormalize( dir );
  580. VectorCopy( NPCInfo->goalEntity->currentOrigin, NPCInfo->blockedTargetPosition );
  581. return qtrue;
  582. }
  583. /*
  584. -------------------------
  585. NAV_GetLastMove
  586. -------------------------
  587. */
  588. void NAV_GetLastMove( navInfo_t &info )
  589. {
  590. info = frameNavInfo;
  591. }
  592. void G_UcmdMoveForDir( gentity_t *self, usercmd_t *cmd, vec3_t dir )
  593. {
  594. vec3_t forward, right;
  595. AngleVectors( self->currentAngles, forward, right, NULL );
  596. dir[2] = 0;
  597. VectorNormalize( dir );
  598. //NPCs cheat and store this directly because converting movement into a ucmd loses precision
  599. VectorCopy( dir, self->client->ps.moveDir );
  600. float fDot = DotProduct( forward, dir ) * 127.0f;
  601. float rDot = DotProduct( right, dir ) * 127.0f;
  602. //Must clamp this because DotProduct is not guaranteed to return a number within -1 to 1, and that would be bad when we're shoving this into a signed byte
  603. if ( fDot > 127.0f )
  604. {
  605. fDot = 127.0f;
  606. }
  607. if ( fDot < -127.0f )
  608. {
  609. fDot = -127.0f;
  610. }
  611. if ( rDot > 127.0f )
  612. {
  613. rDot = 127.0f;
  614. }
  615. if ( rDot < -127.0f )
  616. {
  617. rDot = -127.0f;
  618. }
  619. cmd->forwardmove = floor(fDot);
  620. cmd->rightmove = floor(rDot);
  621. /*
  622. vec3_t wishvel;
  623. for ( int i = 0 ; i < 3 ; i++ )
  624. {
  625. wishvel[i] = forward[i]*cmd->forwardmove + right[i]*cmd->rightmove;
  626. }
  627. VectorNormalize( wishvel );
  628. if ( !VectorCompare( wishvel, dir ) )
  629. {
  630. Com_Printf( "PRECISION LOSS: %s != %s\n", vtos(wishvel), vtos(dir) );
  631. }
  632. */
  633. }
  634. /*
  635. -------------------------
  636. NPC_MoveToGoal
  637. Now assumes goal is goalEntity, was no reason for it to be otherwise
  638. -------------------------
  639. */
  640. #if AI_TIMERS
  641. extern int navTime;
  642. #endif// AI_TIMERS
  643. qboolean NPC_MoveToGoal( qboolean tryStraight ) //FIXME: tryStraight not even used! Stop passing it
  644. {
  645. #if AI_TIMERS
  646. int startTime = GetTime(0);
  647. #endif// AI_TIMERS
  648. if ( PM_InKnockDown( &NPC->client->ps ) || ( ( NPC->client->ps.legsAnim >= BOTH_PAIN1 ) && ( NPC->client->ps.legsAnim <= BOTH_PAIN18 ) && NPC->client->ps.legsAnimTimer > 0 ) )
  649. {//If taking full body pain, don't move
  650. return qtrue;
  651. }
  652. if( NPC->s.eFlags & EF_LOCKED_TO_WEAPON )
  653. {//If in an emplaced gun, never try to navigate!
  654. return qtrue;
  655. }
  656. if( NPC->s.eFlags & EF_HELD_BY_RANCOR )
  657. {//If in a rancor's hand, never try to navigate!
  658. return qtrue;
  659. }
  660. if( NPC->s.eFlags & EF_HELD_BY_WAMPA )
  661. {//If in a wampa's hand, never try to navigate!
  662. return qtrue;
  663. }
  664. if( NPC->s.eFlags & EF_HELD_BY_SAND_CREATURE )
  665. {//If in a worm's mouth, never try to navigate!
  666. return qtrue;
  667. }
  668. if ( NPC->watertype & CONTENTS_LADDER )
  669. {//Do we still want to do this?
  670. vec3_t dir;
  671. VectorSubtract( NPCInfo->goalEntity->currentOrigin, NPC->currentOrigin, dir );
  672. VectorNormalize( dir );
  673. NPC_LadderMove( dir );
  674. }
  675. bool moveSuccess = true;
  676. STEER::Activate(NPC);
  677. {
  678. // Attempt To Steer Directly To Our Goal
  679. //---------------------------------------
  680. moveSuccess = STEER::GoTo(NPC, NPCInfo->goalEntity, NPCInfo->goalRadius);
  681. // Perhaps Not Close Enough? Try To Use The Navigation Grid
  682. //-----------------------------------------------------------
  683. if (!moveSuccess)
  684. {
  685. moveSuccess = NAV::GoTo(NPC, NPCInfo->goalEntity);
  686. if (!moveSuccess)
  687. {
  688. STEER::Stop(NPC);
  689. }
  690. }
  691. }
  692. STEER::DeActivate(NPC, &ucmd);
  693. #if AI_TIMERS
  694. navTime += GetTime( startTime );
  695. #endif// AI_TIMERS
  696. return moveSuccess;
  697. }
  698. /*
  699. -------------------------
  700. void NPC_SlideMoveToGoal( void )
  701. Now assumes goal is goalEntity, if want to use tempGoal, you set that before calling the func
  702. -------------------------
  703. */
  704. qboolean NPC_SlideMoveToGoal( void )
  705. {
  706. float saveYaw = NPC->client->ps.viewangles[YAW];
  707. NPCInfo->combatMove = qtrue;
  708. qboolean ret = NPC_MoveToGoal( qtrue );
  709. NPCInfo->desiredYaw = saveYaw;
  710. return ret;
  711. }
  712. /*
  713. -------------------------
  714. NPC_ApplyRoff
  715. -------------------------
  716. */
  717. void NPC_ApplyRoff(void)
  718. {
  719. PlayerStateToEntityState( &NPC->client->ps, &NPC->s );
  720. VectorCopy ( NPC->currentOrigin, NPC->lastOrigin );
  721. // use the precise origin for linking
  722. gi.linkentity(NPC);
  723. }