NPC_utils.cpp 37 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669
  1. //NPC_utils.cpp
  2. // leave this line at the top for all NPC_xxxx.cpp files...
  3. #include "g_headers.h"
  4. #include "b_local.h"
  5. #include "Q3_Interface.h"
  6. extern Vehicle_t *G_IsRidingVehicle( gentity_t *pEnt );
  7. int teamNumbers[TEAM_NUM_TEAMS];
  8. int teamStrength[TEAM_NUM_TEAMS];
  9. int teamCounter[TEAM_NUM_TEAMS];
  10. #define VALID_ATTACK_CONE 2.0f //Degrees
  11. void GetAnglesForDirection( const vec3_t p1, const vec3_t p2, vec3_t out );
  12. /*
  13. void CalcEntitySpot ( gentity_t *ent, spot_t spot, vec3_t point )
  14. Added: Uses shootAngles if a NPC has them
  15. */
  16. extern void ViewHeightFix(const gentity_t *const ent);
  17. extern void AddLeanOfs(const gentity_t *const ent, vec3_t point);
  18. extern void SubtractLeanOfs(const gentity_t *const ent, vec3_t point);
  19. void CalcEntitySpot ( const gentity_t *ent, const spot_t spot, vec3_t point )
  20. {
  21. vec3_t forward, up, right;
  22. vec3_t start, end;
  23. trace_t tr;
  24. if ( !ent )
  25. {
  26. return;
  27. }
  28. ViewHeightFix(ent);
  29. switch ( spot )
  30. {
  31. case SPOT_ORIGIN:
  32. if(VectorCompare(ent->currentOrigin, vec3_origin))
  33. {//brush
  34. VectorSubtract(ent->absmax, ent->absmin, point);//size
  35. VectorMA(ent->absmin, 0.5, point, point);
  36. }
  37. else
  38. {
  39. VectorCopy ( ent->currentOrigin, point );
  40. }
  41. break;
  42. case SPOT_CHEST:
  43. case SPOT_HEAD:
  44. if ( ent->client && VectorLengthSquared( ent->client->renderInfo.eyePoint ) && (ent->client->ps.viewEntity <= 0 || ent->client->ps.viewEntity >= ENTITYNUM_WORLD) )
  45. {//Actual tag_head eyespot!
  46. //FIXME: Stasis aliens may have a problem here...
  47. VectorCopy( ent->client->renderInfo.eyePoint, point );
  48. if ( ent->client->NPC_class == CLASS_ATST )
  49. {//adjust up some
  50. point[2] += 28;//magic number :)
  51. }
  52. if ( ent->NPC )
  53. {//always aim from the center of my bbox, so we don't wiggle when we lean forward or backwards
  54. point[0] = ent->currentOrigin[0];
  55. point[1] = ent->currentOrigin[1];
  56. }
  57. else if ( !ent->s.number )
  58. {
  59. SubtractLeanOfs( ent, point );
  60. }
  61. }
  62. else
  63. {
  64. VectorCopy ( ent->currentOrigin, point );
  65. if ( ent->client )
  66. {
  67. point[2] += ent->client->ps.viewheight;
  68. }
  69. }
  70. if ( spot == SPOT_CHEST && ent->client )
  71. {
  72. if ( ent->client->NPC_class != CLASS_ATST )
  73. {//adjust up some
  74. point[2] -= ent->maxs[2]*0.2f;
  75. }
  76. }
  77. break;
  78. case SPOT_HEAD_LEAN:
  79. if ( ent->client && VectorLengthSquared( ent->client->renderInfo.eyePoint ) && (ent->client->ps.viewEntity <= 0 || ent->client->ps.viewEntity >= ENTITYNUM_WORLD) )
  80. {//Actual tag_head eyespot!
  81. //FIXME: Stasis aliens may have a problem here...
  82. VectorCopy( ent->client->renderInfo.eyePoint, point );
  83. if ( ent->client->NPC_class == CLASS_ATST )
  84. {//adjust up some
  85. point[2] += 28;//magic number :)
  86. }
  87. if ( ent->NPC )
  88. {//always aim from the center of my bbox, so we don't wiggle when we lean forward or backwards
  89. point[0] = ent->currentOrigin[0];
  90. point[1] = ent->currentOrigin[1];
  91. }
  92. else if ( !ent->s.number )
  93. {
  94. SubtractLeanOfs( ent, point );
  95. }
  96. //NOTE: automatically takes leaning into account!
  97. }
  98. else
  99. {
  100. VectorCopy ( ent->currentOrigin, point );
  101. if ( ent->client )
  102. {
  103. point[2] += ent->client->ps.viewheight;
  104. }
  105. //AddLeanOfs ( ent, point );
  106. }
  107. break;
  108. //FIXME: implement...
  109. //case SPOT_CHEST:
  110. //Returns point 3/4 from tag_torso to tag_head?
  111. //break;
  112. case SPOT_LEGS:
  113. VectorCopy ( ent->currentOrigin, point );
  114. point[2] += (ent->mins[2] * 0.5);
  115. break;
  116. case SPOT_WEAPON:
  117. if( ent->NPC && !VectorCompare( ent->NPC->shootAngles, vec3_origin ) && !VectorCompare( ent->NPC->shootAngles, ent->client->ps.viewangles ))
  118. {
  119. AngleVectors( ent->NPC->shootAngles, forward, right, up );
  120. }
  121. else
  122. {
  123. AngleVectors( ent->client->ps.viewangles, forward, right, up );
  124. }
  125. CalcMuzzlePoint( (gentity_t*)ent, forward, right, up, point, 0 );
  126. //NOTE: automatically takes leaning into account!
  127. break;
  128. case SPOT_GROUND:
  129. // if entity is on the ground, just use it's absmin
  130. if ( ent->s.groundEntityNum != -1 )
  131. {
  132. VectorCopy( ent->currentOrigin, point );
  133. point[2] = ent->absmin[2];
  134. break;
  135. }
  136. // if it is reasonably close to the ground, give the point underneath of it
  137. VectorCopy( ent->currentOrigin, start );
  138. start[2] = ent->absmin[2];
  139. VectorCopy( start, end );
  140. end[2] -= 64;
  141. gi.trace( &tr, start, ent->mins, ent->maxs, end, ent->s.number, MASK_PLAYERSOLID );
  142. if ( tr.fraction < 1.0 )
  143. {
  144. VectorCopy( tr.endpos, point);
  145. break;
  146. }
  147. // otherwise just use the origin
  148. VectorCopy( ent->currentOrigin, point );
  149. break;
  150. default:
  151. VectorCopy ( ent->currentOrigin, point );
  152. break;
  153. }
  154. }
  155. //===================================================================================
  156. /*
  157. qboolean NPC_UpdateAngles ( qboolean doPitch, qboolean doYaw )
  158. Added: option to do just pitch or just yaw
  159. Does not include "aim" in it's calculations
  160. FIXME: stop compressing angles into shorts!!!!
  161. */
  162. extern cvar_t *g_timescale;
  163. extern bool NPC_IsTrooper( gentity_t *ent );
  164. qboolean NPC_UpdateAngles ( qboolean doPitch, qboolean doYaw )
  165. {
  166. #if 1
  167. float error;
  168. float decay;
  169. float targetPitch = 0;
  170. float targetYaw = 0;
  171. float yawSpeed;
  172. qboolean exact = qtrue;
  173. // if angle changes are locked; just keep the current angles
  174. // aimTime isn't even set anymore... so this code was never reached, but I need a way to lock NPC's yaw, so instead of making a new SCF_ flag, just use the existing render flag... - dmv
  175. if ( !NPC->enemy && ( (level.time < NPCInfo->aimTime) || NPC->client->renderInfo.renderFlags & RF_LOCKEDANGLE) )
  176. {
  177. if(doPitch)
  178. targetPitch = NPCInfo->lockedDesiredPitch;
  179. if(doYaw)
  180. targetYaw = NPCInfo->lockedDesiredYaw;
  181. }
  182. else
  183. {
  184. // we're changing the lockedDesired Pitch/Yaw below so it's lost it's original meaning, get rid of the lock flag
  185. NPC->client->renderInfo.renderFlags &= ~RF_LOCKEDANGLE;
  186. if(doPitch)
  187. {
  188. targetPitch = NPCInfo->desiredPitch;
  189. NPCInfo->lockedDesiredPitch = NPCInfo->desiredPitch;
  190. }
  191. if(doYaw)
  192. {
  193. targetYaw = NPCInfo->desiredYaw;
  194. NPCInfo->lockedDesiredYaw = NPCInfo->desiredYaw;
  195. }
  196. }
  197. if ( NPC->s.weapon == WP_EMPLACED_GUN )
  198. {
  199. // FIXME: this seems to do nothing, actually...
  200. yawSpeed = 20;
  201. }
  202. else
  203. {
  204. if ( NPC->client->NPC_class == CLASS_ROCKETTROOPER
  205. && !NPC->enemy )
  206. {//just slowly lookin' around
  207. yawSpeed = 1;
  208. }
  209. else
  210. {
  211. yawSpeed = NPCInfo->stats.yawSpeed;
  212. }
  213. }
  214. if ( NPC->s.weapon == WP_SABER && NPC->client->ps.forcePowersActive&(1<<FP_SPEED) )
  215. {
  216. yawSpeed *= 1.0f/g_timescale->value;
  217. }
  218. if (!NPC_IsTrooper(NPC)
  219. && NPC->enemy
  220. && !G_IsRidingVehicle( NPC )
  221. && NPC->client->NPC_class != CLASS_VEHICLE )
  222. {
  223. if (NPC->s.weapon==WP_BLASTER_PISTOL ||
  224. NPC->s.weapon==WP_BLASTER ||
  225. NPC->s.weapon==WP_BOWCASTER ||
  226. NPC->s.weapon==WP_REPEATER ||
  227. NPC->s.weapon==WP_FLECHETTE ||
  228. NPC->s.weapon==WP_BRYAR_PISTOL ||
  229. NPC->s.weapon==WP_NOGHRI_STICK)
  230. {
  231. yawSpeed *= 10.0f;
  232. }
  233. }
  234. if( doYaw )
  235. {
  236. // decay yaw error
  237. error = AngleDelta ( NPC->client->ps.viewangles[YAW], targetYaw );
  238. if( fabs(error) > MIN_ANGLE_ERROR )
  239. {
  240. if ( error )
  241. {
  242. exact = qfalse;
  243. decay = 60.0 + yawSpeed * 3;
  244. decay *= 50.0f / 1000.0f;//msec
  245. if ( error < 0.0 )
  246. {
  247. error += decay;
  248. if ( error > 0.0 )
  249. {
  250. error = 0.0;
  251. }
  252. }
  253. else
  254. {
  255. error -= decay;
  256. if ( error < 0.0 )
  257. {
  258. error = 0.0;
  259. }
  260. }
  261. }
  262. }
  263. ucmd.angles[YAW] = ANGLE2SHORT( targetYaw + error ) - client->ps.delta_angles[YAW];
  264. }
  265. //FIXME: have a pitchSpeed?
  266. if( doPitch )
  267. {
  268. // decay pitch error
  269. error = AngleDelta ( NPC->client->ps.viewangles[PITCH], targetPitch );
  270. if ( fabs(error) > MIN_ANGLE_ERROR )
  271. {
  272. if ( error )
  273. {
  274. exact = qfalse;
  275. decay = 60.0 + yawSpeed * 3;
  276. decay *= 50.0f / 1000.0f;//msec
  277. if ( error < 0.0 )
  278. {
  279. error += decay;
  280. if ( error > 0.0 )
  281. {
  282. error = 0.0;
  283. }
  284. }
  285. else
  286. {
  287. error -= decay;
  288. if ( error < 0.0 )
  289. {
  290. error = 0.0;
  291. }
  292. }
  293. }
  294. }
  295. ucmd.angles[PITCH] = ANGLE2SHORT( targetPitch + error ) - client->ps.delta_angles[PITCH];
  296. }
  297. ucmd.angles[ROLL] = ANGLE2SHORT ( NPC->client->ps.viewangles[ROLL] ) - client->ps.delta_angles[ROLL];
  298. if ( exact && Q3_TaskIDPending( NPC, TID_ANGLE_FACE ) )
  299. {
  300. Q3_TaskIDComplete( NPC, TID_ANGLE_FACE );
  301. }
  302. return exact;
  303. #else
  304. float error;
  305. float decay;
  306. float targetPitch = 0;
  307. float targetYaw = 0;
  308. float yawSpeed;
  309. //float runningMod = NPCInfo->currentSpeed/100.0f;
  310. qboolean exact = qtrue;
  311. qboolean doSound = qfalse;
  312. // if angle changes are locked; just keep the current angles
  313. if ( level.time < NPCInfo->aimTime )
  314. {
  315. if(doPitch)
  316. targetPitch = NPCInfo->lockedDesiredPitch;
  317. if(doYaw)
  318. targetYaw = NPCInfo->lockedDesiredYaw;
  319. }
  320. else
  321. {
  322. if(doPitch)
  323. targetPitch = NPCInfo->desiredPitch;
  324. if(doYaw)
  325. targetYaw = NPCInfo->desiredYaw;
  326. // NPCInfo->aimTime = level.time + 250;
  327. if(doPitch)
  328. NPCInfo->lockedDesiredPitch = NPCInfo->desiredPitch;
  329. if(doYaw)
  330. NPCInfo->lockedDesiredYaw = NPCInfo->desiredYaw;
  331. }
  332. yawSpeed = NPCInfo->stats.yawSpeed;
  333. if(doYaw)
  334. {
  335. // decay yaw error
  336. error = AngleDelta ( NPC->client->ps.viewangles[YAW], targetYaw );
  337. if( fabs(error) > MIN_ANGLE_ERROR )
  338. {
  339. /*
  340. if(NPC->client->playerTeam == TEAM_BORG&&
  341. NPCInfo->behaviorState != BS_FACE&&NPCInfo->tempBehavior!= BS_FACE)
  342. {//HACK - borg turn more jittery
  343. if ( error )
  344. {
  345. exact = qfalse;
  346. decay = 60.0 + yawSpeed * 3;
  347. decay *= 50.0 / 1000.0;//msec
  348. //Snap to
  349. if(fabs(error) > 10)
  350. {
  351. if(random() > 0.6)
  352. {
  353. doSound = qtrue;
  354. }
  355. }
  356. if ( error < 0.0)//-10.0 )
  357. {
  358. error += decay;
  359. if ( error > 0.0 )
  360. {
  361. error = 0.0;
  362. }
  363. }
  364. else if ( error > 0.0)//10.0 )
  365. {
  366. error -= decay;
  367. if ( error < 0.0 )
  368. {
  369. error = 0.0;
  370. }
  371. }
  372. }
  373. }
  374. else*/
  375. if ( error )
  376. {
  377. exact = qfalse;
  378. decay = 60.0 + yawSpeed * 3;
  379. decay *= 50.0 / 1000.0;//msec
  380. if ( error < 0.0 )
  381. {
  382. error += decay;
  383. if ( error > 0.0 )
  384. {
  385. error = 0.0;
  386. }
  387. }
  388. else
  389. {
  390. error -= decay;
  391. if ( error < 0.0 )
  392. {
  393. error = 0.0;
  394. }
  395. }
  396. }
  397. }
  398. ucmd.angles[YAW] = ANGLE2SHORT( targetYaw + error ) - client->ps.delta_angles[YAW];
  399. }
  400. //FIXME: have a pitchSpeed?
  401. if(doPitch)
  402. {
  403. // decay pitch error
  404. error = AngleDelta ( NPC->client->ps.viewangles[PITCH], targetPitch );
  405. if ( fabs(error) > MIN_ANGLE_ERROR )
  406. {
  407. /*
  408. if(NPC->client->playerTeam == TEAM_BORG&&
  409. NPCInfo->behaviorState != BS_FACE&&NPCInfo->tempBehavior!= BS_FACE)
  410. {//HACK - borg turn more jittery
  411. if ( error )
  412. {
  413. exact = qfalse;
  414. decay = 60.0 + yawSpeed * 3;
  415. decay *= 50.0 / 1000.0;//msec
  416. //Snap to
  417. if(fabs(error) > 10)
  418. {
  419. if(random() > 0.6)
  420. {
  421. doSound = qtrue;
  422. }
  423. }
  424. if ( error < 0.0)//-10.0 )
  425. {
  426. error += decay;
  427. if ( error > 0.0 )
  428. {
  429. error = 0.0;
  430. }
  431. }
  432. else if ( error > 0.0)//10.0 )
  433. {
  434. error -= decay;
  435. if ( error < 0.0 )
  436. {
  437. error = 0.0;
  438. }
  439. }
  440. }
  441. }
  442. else*/
  443. if ( error )
  444. {
  445. exact = qfalse;
  446. decay = 60.0 + yawSpeed * 3;
  447. decay *= 50.0 / 1000.0;//msec
  448. if ( error < 0.0 )
  449. {
  450. error += decay;
  451. if ( error > 0.0 )
  452. {
  453. error = 0.0;
  454. }
  455. }
  456. else
  457. {
  458. error -= decay;
  459. if ( error < 0.0 )
  460. {
  461. error = 0.0;
  462. }
  463. }
  464. }
  465. }
  466. ucmd.angles[PITCH] = ANGLE2SHORT( targetPitch + error ) - client->ps.delta_angles[PITCH];
  467. }
  468. ucmd.angles[ROLL] = ANGLE2SHORT ( NPC->client->ps.viewangles[ROLL] ) - client->ps.delta_angles[ROLL];
  469. /*
  470. if(doSound)
  471. {
  472. G_Sound(NPC, G_SoundIndex(va("sound/enemies/borg/borgservo%d.wav", Q_irand(1, 8))));
  473. }
  474. */
  475. return exact;
  476. #endif
  477. }
  478. void NPC_AimWiggle( vec3_t enemy_org )
  479. {
  480. //shoot for somewhere between the head and torso
  481. //NOTE: yes, I know this looks weird, but it works
  482. if ( NPCInfo->aimErrorDebounceTime < level.time )
  483. {
  484. NPCInfo->aimOfs[0] = 0.3*Q_flrand(NPC->enemy->mins[0], NPC->enemy->maxs[0]);
  485. NPCInfo->aimOfs[1] = 0.3*Q_flrand(NPC->enemy->mins[1], NPC->enemy->maxs[1]);
  486. if ( NPC->enemy->maxs[2] > 0 )
  487. {
  488. NPCInfo->aimOfs[2] = NPC->enemy->maxs[2]*Q_flrand(0.0f, -1.0f);
  489. }
  490. }
  491. VectorAdd( enemy_org, NPCInfo->aimOfs, enemy_org );
  492. }
  493. /*
  494. qboolean NPC_UpdateFiringAngles ( qboolean doPitch, qboolean doYaw )
  495. Includes aim when determining angles - so they don't always hit...
  496. */
  497. qboolean NPC_UpdateFiringAngles ( qboolean doPitch, qboolean doYaw )
  498. {
  499. #if 0
  500. float diff;
  501. float error;
  502. float targetPitch = 0;
  503. float targetYaw = 0;
  504. qboolean exact = qtrue;
  505. if ( level.time < NPCInfo->aimTime )
  506. {
  507. if( doPitch )
  508. targetPitch = NPCInfo->lockedDesiredPitch;
  509. if( doYaw )
  510. targetYaw = NPCInfo->lockedDesiredYaw;
  511. }
  512. else
  513. {
  514. if( doPitch )
  515. {
  516. targetPitch = NPCInfo->desiredPitch;
  517. NPCInfo->lockedDesiredPitch = NPCInfo->desiredPitch;
  518. }
  519. if( doYaw )
  520. {
  521. targetYaw = NPCInfo->desiredYaw;
  522. NPCInfo->lockedDesiredYaw = NPCInfo->desiredYaw;
  523. }
  524. }
  525. if( doYaw )
  526. {
  527. // add yaw error based on NPCInfo->aim value
  528. error = ((float)(6 - NPCInfo->stats.aim)) * Q_flrand(-1, 1);
  529. if(Q_irand(0, 1))
  530. error *= -1;
  531. diff = AngleDelta ( NPC->client->ps.viewangles[YAW], targetYaw );
  532. if ( diff )
  533. exact = qfalse;
  534. ucmd.angles[YAW] = ANGLE2SHORT( targetYaw + diff + error ) - client->ps.delta_angles[YAW];
  535. }
  536. if( doPitch )
  537. {
  538. // add pitch error based on NPCInfo->aim value
  539. error = ((float)(6 - NPCInfo->stats.aim)) * Q_flrand(-1, 1);
  540. diff = AngleDelta ( NPC->client->ps.viewangles[PITCH], targetPitch );
  541. if ( diff )
  542. exact = qfalse;
  543. ucmd.angles[PITCH] = ANGLE2SHORT( targetPitch + diff + error ) - client->ps.delta_angles[PITCH];
  544. }
  545. ucmd.angles[ROLL] = ANGLE2SHORT ( NPC->client->ps.viewangles[ROLL] ) - client->ps.delta_angles[ROLL];
  546. return exact;
  547. #else
  548. float error, diff;
  549. float decay;
  550. float targetPitch = 0;
  551. float targetYaw = 0;
  552. qboolean exact = qtrue;
  553. // if angle changes are locked; just keep the current angles
  554. if ( level.time < NPCInfo->aimTime )
  555. {
  556. if(doPitch)
  557. targetPitch = NPCInfo->lockedDesiredPitch;
  558. if(doYaw)
  559. targetYaw = NPCInfo->lockedDesiredYaw;
  560. }
  561. else
  562. {
  563. if(doPitch)
  564. targetPitch = NPCInfo->desiredPitch;
  565. if(doYaw)
  566. targetYaw = NPCInfo->desiredYaw;
  567. // NPCInfo->aimTime = level.time + 250;
  568. if(doPitch)
  569. NPCInfo->lockedDesiredPitch = NPCInfo->desiredPitch;
  570. if(doYaw)
  571. NPCInfo->lockedDesiredYaw = NPCInfo->desiredYaw;
  572. }
  573. if ( NPCInfo->aimErrorDebounceTime < level.time )
  574. {
  575. if ( Q_irand(0, 1 ) )
  576. {
  577. NPCInfo->lastAimErrorYaw = ((float)(6 - NPCInfo->stats.aim)) * Q_flrand(-1, 1);
  578. }
  579. if ( Q_irand(0, 1 ) )
  580. {
  581. NPCInfo->lastAimErrorPitch = ((float)(6 - NPCInfo->stats.aim)) * Q_flrand(-1, 1);
  582. }
  583. NPCInfo->aimErrorDebounceTime = level.time + Q_irand(250, 2000);
  584. }
  585. if(doYaw)
  586. {
  587. // decay yaw diff
  588. diff = AngleDelta ( NPC->client->ps.viewangles[YAW], targetYaw );
  589. if ( diff)
  590. {
  591. exact = qfalse;
  592. decay = 60.0 + 80.0;
  593. decay *= 50.0f / 1000.0f;//msec
  594. if ( diff < 0.0 )
  595. {
  596. diff += decay;
  597. if ( diff > 0.0 )
  598. {
  599. diff = 0.0;
  600. }
  601. }
  602. else
  603. {
  604. diff -= decay;
  605. if ( diff < 0.0 )
  606. {
  607. diff = 0.0;
  608. }
  609. }
  610. }
  611. // add yaw error based on NPCInfo->aim value
  612. error = NPCInfo->lastAimErrorYaw;
  613. /*
  614. if(Q_irand(0, 1))
  615. {
  616. error *= -1;
  617. }
  618. */
  619. ucmd.angles[YAW] = ANGLE2SHORT( targetYaw + diff + error ) - client->ps.delta_angles[YAW];
  620. }
  621. if(doPitch)
  622. {
  623. // decay pitch diff
  624. diff = AngleDelta ( NPC->client->ps.viewangles[PITCH], targetPitch );
  625. if ( diff)
  626. {
  627. exact = qfalse;
  628. decay = 60.0 + 80.0;
  629. decay *= 50.0f / 1000.0f;//msec
  630. if ( diff < 0.0 )
  631. {
  632. diff += decay;
  633. if ( diff > 0.0 )
  634. {
  635. diff = 0.0;
  636. }
  637. }
  638. else
  639. {
  640. diff -= decay;
  641. if ( diff < 0.0 )
  642. {
  643. diff = 0.0;
  644. }
  645. }
  646. }
  647. error = NPCInfo->lastAimErrorPitch;
  648. ucmd.angles[PITCH] = ANGLE2SHORT( targetPitch + diff + error ) - client->ps.delta_angles[PITCH];
  649. }
  650. ucmd.angles[ROLL] = ANGLE2SHORT ( NPC->client->ps.viewangles[ROLL] ) - client->ps.delta_angles[ROLL];
  651. return exact;
  652. #endif
  653. }
  654. //===================================================================================
  655. /*
  656. static void NPC_UpdateShootAngles (vec3_t angles, qboolean doPitch, qboolean doYaw )
  657. Does update angles on shootAngles
  658. */
  659. void NPC_UpdateShootAngles (vec3_t angles, qboolean doPitch, qboolean doYaw )
  660. {//FIXME: shoot angles either not set right or not used!
  661. float error;
  662. float decay;
  663. float targetPitch = 0;
  664. float targetYaw = 0;
  665. if(doPitch)
  666. targetPitch = angles[PITCH];
  667. if(doYaw)
  668. targetYaw = angles[YAW];
  669. if(doYaw)
  670. {
  671. // decay yaw error
  672. error = AngleDelta ( NPCInfo->shootAngles[YAW], targetYaw );
  673. if ( error )
  674. {
  675. decay = 60.0 + 80.0 * NPCInfo->stats.aim;
  676. decay *= 100.0f / 1000.0f;//msec
  677. if ( error < 0.0 )
  678. {
  679. error += decay;
  680. if ( error > 0.0 )
  681. {
  682. error = 0.0;
  683. }
  684. }
  685. else
  686. {
  687. error -= decay;
  688. if ( error < 0.0 )
  689. {
  690. error = 0.0;
  691. }
  692. }
  693. }
  694. NPCInfo->shootAngles[YAW] = targetYaw + error;
  695. }
  696. if(doPitch)
  697. {
  698. // decay pitch error
  699. error = AngleDelta ( NPCInfo->shootAngles[PITCH], targetPitch );
  700. if ( error )
  701. {
  702. decay = 60.0 + 80.0 * NPCInfo->stats.aim;
  703. decay *= 100.0f / 1000.0f;//msec
  704. if ( error < 0.0 )
  705. {
  706. error += decay;
  707. if ( error > 0.0 )
  708. {
  709. error = 0.0;
  710. }
  711. }
  712. else
  713. {
  714. error -= decay;
  715. if ( error < 0.0 )
  716. {
  717. error = 0.0;
  718. }
  719. }
  720. }
  721. NPCInfo->shootAngles[PITCH] = targetPitch + error;
  722. }
  723. }
  724. /*
  725. void SetTeamNumbers (void)
  726. Sets the number of living clients on each team
  727. FIXME: Does not account for non-respawned players!
  728. FIXME: Don't include medics?
  729. */
  730. void SetTeamNumbers (void)
  731. {
  732. gentity_t *found;
  733. int i;
  734. for( i = 0; i < TEAM_NUM_TEAMS; i++ )
  735. {
  736. teamNumbers[i] = 0;
  737. teamStrength[i] = 0;
  738. }
  739. for( i = 0; i < 1 ; i++ )
  740. {
  741. found = &g_entities[i];
  742. if( found->client )
  743. {
  744. if( found->health > 0 )//FIXME: or if a player!
  745. {
  746. teamNumbers[found->client->playerTeam]++;
  747. teamStrength[found->client->playerTeam] += found->health;
  748. }
  749. }
  750. }
  751. for( i = 0; i < TEAM_NUM_TEAMS; i++ )
  752. {//Get the average health
  753. teamStrength[i] = floor( ((float)(teamStrength[i])) / ((float)(teamNumbers[i])) );
  754. }
  755. }
  756. extern stringID_table_t BSTable[];
  757. extern stringID_table_t BSETTable[];
  758. qboolean G_ActivateBehavior (gentity_t *self, int bset )
  759. {
  760. bState_t bSID = (bState_t)-1;
  761. char *bs_name = NULL;
  762. if ( !self )
  763. {
  764. return qfalse;
  765. }
  766. bs_name = self->behaviorSet[bset];
  767. if( !(VALIDSTRING( bs_name )) )
  768. {
  769. return qfalse;
  770. }
  771. if ( self->NPC )
  772. {
  773. bSID = (bState_t)(GetIDForString( BSTable, bs_name ));
  774. }
  775. if(bSID > -1)
  776. {
  777. self->NPC->tempBehavior = BS_DEFAULT;
  778. self->NPC->behaviorState = bSID;
  779. if ( bSID == BS_SEARCH || bSID == BS_WANDER )
  780. {
  781. //FIXME: Reimplement?
  782. if( self->waypoint != WAYPOINT_NONE )
  783. {
  784. NPC_BSSearchStart( self->waypoint, bSID );
  785. }
  786. else
  787. {
  788. self->waypoint = NAV::GetNearestNode(self);
  789. if( self->waypoint != WAYPOINT_NONE )
  790. {
  791. NPC_BSSearchStart( self->waypoint, bSID );
  792. }
  793. }
  794. }
  795. }
  796. else
  797. {
  798. Quake3Game()->DebugPrint( IGameInterface::WL_VERBOSE, "%s attempting to run bSet %s (%s)\n", self->targetname, GetStringForID( BSETTable, bset ), bs_name );
  799. Quake3Game()->RunScript( self, bs_name );
  800. }
  801. return qtrue;
  802. }
  803. /*
  804. =============================================================================
  805. Extended Functions
  806. =============================================================================
  807. */
  808. /*
  809. -------------------------
  810. NPC_ValidEnemy
  811. -------------------------
  812. */
  813. qboolean G_ValidEnemy( gentity_t *self, gentity_t *enemy )
  814. {
  815. //Must be a valid pointer
  816. if ( enemy == NULL )
  817. return qfalse;
  818. //Must not be me
  819. if ( enemy == self )
  820. return qfalse;
  821. //Must not be deleted
  822. if ( enemy->inuse == qfalse )
  823. return qfalse;
  824. //Must be alive
  825. if ( enemy->health <= 0 )
  826. return qfalse;
  827. //In case they're in notarget mode
  828. if ( enemy->flags & FL_NOTARGET )
  829. return qfalse;
  830. //Must be an NPC
  831. if ( enemy->client == NULL )
  832. {
  833. if ( enemy->svFlags&SVF_NONNPC_ENEMY )
  834. {//still potentially valid
  835. if (self->client)
  836. {
  837. if ( enemy->noDamageTeam == self->client->playerTeam )
  838. {
  839. return qfalse;
  840. }
  841. else
  842. {
  843. return qtrue;
  844. }
  845. }
  846. else
  847. {
  848. if ( enemy->noDamageTeam == self->noDamageTeam )
  849. {
  850. return qfalse;
  851. }
  852. else
  853. {
  854. return qtrue;
  855. }
  856. }
  857. }
  858. else
  859. {
  860. return qfalse;
  861. }
  862. }
  863. if ( enemy->client->playerTeam == TEAM_FREE && enemy->s.number < MAX_CLIENTS )
  864. {//An evil player, everyone attacks him
  865. return qtrue;
  866. }
  867. //Can't be on the same team
  868. if ( enemy->client->playerTeam == self->client->playerTeam )
  869. {
  870. return qfalse;
  871. }
  872. //if haven't seen him in a while, give up
  873. //if ( NPCInfo->enemyLastSeenTime != 0 && level.time - NPCInfo->enemyLastSeenTime > 7000 )//FIXME: make a stat?
  874. // return qfalse;
  875. if ( enemy->client->playerTeam == self->client->enemyTeam //simplest case: they're on my enemy team
  876. || (self->client->enemyTeam == TEAM_FREE && enemy->client->NPC_class != self->client->NPC_class )//I get mad at anyone and this guy isn't the same class as me
  877. || (enemy->client->NPC_class == CLASS_WAMPA && enemy->enemy )//a rampaging wampa
  878. || (enemy->client->NPC_class == CLASS_RANCOR && enemy->enemy )//a rampaging rancor
  879. || (enemy->client->playerTeam == TEAM_FREE && enemy->client->enemyTeam == TEAM_FREE && enemy->enemy && enemy->enemy->client && (enemy->enemy->client->playerTeam == self->client->playerTeam||(enemy->enemy->client->playerTeam != TEAM_ENEMY&&self->client->playerTeam==TEAM_PLAYER))) //enemy is a rampaging non-aligned creature who is attacking someone on our team or a non-enemy (this last condition is used only if we're a good guy - in effect, we protect the innocent)
  880. )
  881. {
  882. return qtrue;
  883. }
  884. //all other cases = false?
  885. return qfalse;
  886. }
  887. qboolean NPC_ValidEnemy( gentity_t *ent )
  888. {
  889. return G_ValidEnemy( NPC, ent );
  890. }
  891. /*
  892. -------------------------
  893. NPC_TargetVisible
  894. -------------------------
  895. */
  896. qboolean NPC_TargetVisible( gentity_t *ent )
  897. {
  898. //Make sure we're in a valid range
  899. if ( DistanceSquared( ent->currentOrigin, NPC->currentOrigin ) > ( NPCInfo->stats.visrange * NPCInfo->stats.visrange ) )
  900. return qfalse;
  901. //Check our FOV
  902. if ( InFOV( ent, NPC, NPCInfo->stats.hfov, NPCInfo->stats.vfov ) == qfalse )
  903. return qfalse;
  904. //Check for sight
  905. if ( NPC_ClearLOS( ent ) == qfalse )
  906. return qfalse;
  907. return qtrue;
  908. }
  909. /*
  910. -------------------------
  911. NPC_GetCheckDelta
  912. -------------------------
  913. */
  914. /*
  915. #define CHECK_TIME_BASE 250
  916. #define CHECK_TIME_BASE_SQUARED ( CHECK_TIME_BASE * CHECK_TIME_BASE )
  917. static int NPC_GetCheckDelta( void )
  918. {
  919. if ( NPC_ValidEnemy( NPC->enemy ) == qfalse )
  920. {
  921. int distance = DistanceSquared( NPC->currentOrigin, g_entities[0].currentOrigin );
  922. distance /= CHECK_TIME_BASE_SQUARED;
  923. return ( CHECK_TIME_BASE * distance );
  924. }
  925. return 0;
  926. }
  927. */
  928. /*
  929. -------------------------
  930. NPC_FindNearestEnemy
  931. -------------------------
  932. */
  933. #define MAX_RADIUS_ENTS 256 //NOTE: This can cause entities to be lost
  934. #define NEAR_DEFAULT_RADIUS 256
  935. extern gentity_t *G_CheckControlledTurretEnemy(gentity_t *self, gentity_t *enemy, qboolean validate );
  936. int NPC_FindNearestEnemy( gentity_t *ent )
  937. {
  938. gentity_t *radiusEnts[ MAX_RADIUS_ENTS ];
  939. gentity_t *nearest;
  940. vec3_t mins, maxs;
  941. int nearestEntID = -1;
  942. float nearestDist = (float)WORLD_SIZE*(float)WORLD_SIZE;
  943. float distance;
  944. int numEnts, numChecks = 0;
  945. //Setup the bbox to search in
  946. for ( int i = 0; i < 3; i++ )
  947. {
  948. mins[i] = ent->currentOrigin[i] - NPCInfo->stats.visrange;
  949. maxs[i] = ent->currentOrigin[i] + NPCInfo->stats.visrange;
  950. }
  951. //Get a number of entities in a given space
  952. numEnts = gi.EntitiesInBox( mins, maxs, radiusEnts, MAX_RADIUS_ENTS );
  953. for ( i = 0; i < numEnts; i++ )
  954. {
  955. nearest = G_CheckControlledTurretEnemy(ent, radiusEnts[i], qtrue);
  956. //Don't consider self
  957. if ( nearest == ent )
  958. continue;
  959. //Must be valid
  960. if ( NPC_ValidEnemy( nearest ) == qfalse )
  961. continue;
  962. numChecks++;
  963. //Must be visible
  964. if ( NPC_TargetVisible( nearest ) == qfalse )
  965. continue;
  966. distance = DistanceSquared( ent->currentOrigin, nearest->currentOrigin );
  967. //Found one closer to us
  968. if ( distance < nearestDist )
  969. {
  970. nearestEntID = nearest->s.number;
  971. nearestDist = distance;
  972. }
  973. }
  974. return nearestEntID;
  975. }
  976. /*
  977. -------------------------
  978. NPC_PickEnemyExt
  979. -------------------------
  980. */
  981. gentity_t *NPC_PickEnemyExt( qboolean checkAlerts = qfalse )
  982. {
  983. //Check for Hazard Team status and remove this check
  984. /*
  985. if ( NPC->client->playerTeam != TEAM_STARFLEET )
  986. {
  987. //If we've found the player, return it
  988. if ( NPC_FindPlayer() )
  989. return &g_entities[0];
  990. }
  991. */
  992. //If we've asked for the closest enemy
  993. int entID = NPC_FindNearestEnemy( NPC );
  994. //If we have a valid enemy, use it
  995. if ( entID >= 0 )
  996. return &g_entities[entID];
  997. if ( checkAlerts )
  998. {
  999. int alertEvent = NPC_CheckAlertEvents( qtrue, qtrue, -1, qtrue, AEL_DISCOVERED );
  1000. //There is an event to look at
  1001. if ( alertEvent >= 0 )
  1002. {
  1003. alertEvent_t *event = &level.alertEvents[alertEvent];
  1004. //Don't pay attention to our own alerts
  1005. if ( event->owner == NPC )
  1006. return NULL;
  1007. if ( event->level >= AEL_DISCOVERED )
  1008. {
  1009. //If it's the player, attack him
  1010. if ( event->owner == &g_entities[0] )
  1011. return event->owner;
  1012. //If it's on our team, then take its enemy as well
  1013. if ( ( event->owner->client ) && ( event->owner->client->playerTeam == NPC->client->playerTeam ) )
  1014. return event->owner->enemy;
  1015. }
  1016. }
  1017. }
  1018. return NULL;
  1019. }
  1020. /*
  1021. -------------------------
  1022. NPC_FindPlayer
  1023. -------------------------
  1024. */
  1025. qboolean NPC_FindPlayer( void )
  1026. {
  1027. return NPC_TargetVisible( &g_entities[0] );
  1028. }
  1029. /*
  1030. -------------------------
  1031. NPC_CheckPlayerDistance
  1032. -------------------------
  1033. */
  1034. static qboolean NPC_CheckPlayerDistance( void )
  1035. {
  1036. //Make sure we have an enemy
  1037. if ( NPC->enemy == NULL )
  1038. return qfalse;
  1039. //Only do this for non-players
  1040. if ( NPC->enemy->s.number == 0 )
  1041. return qfalse;
  1042. //must be set up to get mad at player
  1043. if ( !NPC->client || NPC->client->enemyTeam != TEAM_PLAYER )
  1044. return qfalse;
  1045. //Must be within our FOV
  1046. if ( InFOV( &g_entities[0], NPC, NPCInfo->stats.hfov, NPCInfo->stats.vfov ) == qfalse )
  1047. return qfalse;
  1048. float distance = DistanceSquared( NPC->currentOrigin, NPC->enemy->currentOrigin );
  1049. if ( distance > DistanceSquared( NPC->currentOrigin, g_entities[0].currentOrigin ) )
  1050. {
  1051. G_SetEnemy( NPC, &g_entities[0] );
  1052. return qtrue;
  1053. }
  1054. return qfalse;
  1055. }
  1056. /*
  1057. -------------------------
  1058. NPC_FindEnemy
  1059. -------------------------
  1060. */
  1061. qboolean NPC_FindEnemy( qboolean checkAlerts = qfalse )
  1062. {
  1063. //We're ignoring all enemies for now
  1064. if( NPC->svFlags & SVF_IGNORE_ENEMIES )
  1065. {
  1066. G_ClearEnemy( NPC );
  1067. return qfalse;
  1068. }
  1069. //we can't pick up any enemies for now
  1070. if( NPCInfo->confusionTime > level.time )
  1071. {
  1072. G_ClearEnemy( NPC );
  1073. return qfalse;
  1074. }
  1075. //Don't want a new enemy
  1076. if ( ( NPC_ValidEnemy( NPC->enemy ) ) && ( NPC->svFlags & SVF_LOCKEDENEMY ) )
  1077. return qtrue;
  1078. //See if the player is closer than our current enemy
  1079. if ( NPC->client->NPC_class != CLASS_RANCOR
  1080. && NPC->client->NPC_class != CLASS_WAMPA
  1081. && NPC->client->NPC_class != CLASS_SAND_CREATURE
  1082. && NPC_CheckPlayerDistance() )
  1083. {//rancors, wampas & sand creatures don't care if player is closer, they always go with closest
  1084. return qtrue;
  1085. }
  1086. //Otherwise, turn off the flag
  1087. NPC->svFlags &= ~SVF_LOCKEDENEMY;
  1088. //If we've gotten here alright, then our target it still valid
  1089. if ( NPC_ValidEnemy( NPC->enemy ) )
  1090. return qtrue;
  1091. gentity_t *newenemy = NPC_PickEnemyExt( checkAlerts );
  1092. //if we found one, take it as the enemy
  1093. if( NPC_ValidEnemy( newenemy ) )
  1094. {
  1095. G_SetEnemy( NPC, newenemy );
  1096. return qtrue;
  1097. }
  1098. G_ClearEnemy( NPC );
  1099. return qfalse;
  1100. }
  1101. /*
  1102. -------------------------
  1103. NPC_CheckEnemyExt
  1104. -------------------------
  1105. */
  1106. qboolean NPC_CheckEnemyExt( qboolean checkAlerts )
  1107. {
  1108. //Make sure we're ready to think again
  1109. /*
  1110. if ( NPCInfo->enemyCheckDebounceTime > level.time )
  1111. return qfalse;
  1112. //Get our next think time
  1113. NPCInfo->enemyCheckDebounceTime = level.time + NPC_GetCheckDelta();
  1114. //Attempt to find an enemy
  1115. return NPC_FindEnemy();
  1116. */
  1117. return NPC_FindEnemy( checkAlerts );
  1118. }
  1119. /*
  1120. -------------------------
  1121. NPC_FacePosition
  1122. -------------------------
  1123. */
  1124. qboolean NPC_FacePosition( vec3_t position, qboolean doPitch )
  1125. {
  1126. vec3_t muzzle;
  1127. qboolean facing = qtrue;
  1128. //Get the positions
  1129. if ( NPC->client && (NPC->client->NPC_class == CLASS_RANCOR || NPC->client->NPC_class == CLASS_WAMPA || NPC->client->NPC_class == CLASS_SAND_CREATURE) )
  1130. {
  1131. CalcEntitySpot( NPC, SPOT_ORIGIN, muzzle );
  1132. muzzle[2] += NPC->maxs[2] * 0.75f;
  1133. }
  1134. else if ( NPC->client && NPC->client->NPC_class == CLASS_GALAKMECH )
  1135. {
  1136. CalcEntitySpot( NPC, SPOT_WEAPON, muzzle );
  1137. }
  1138. else
  1139. {
  1140. CalcEntitySpot( NPC, SPOT_HEAD_LEAN, muzzle );//SPOT_HEAD
  1141. if ( NPC->client->NPC_class == CLASS_ROCKETTROOPER )
  1142. {//*sigh*, look down more
  1143. position[2] -= 32;
  1144. }
  1145. }
  1146. //Find the desired angles
  1147. vec3_t angles;
  1148. GetAnglesForDirection( muzzle, position, angles );
  1149. NPCInfo->desiredYaw = AngleNormalize360( angles[YAW] );
  1150. NPCInfo->desiredPitch = AngleNormalize360( angles[PITCH] );
  1151. if ( NPC->enemy && NPC->enemy->client && NPC->enemy->client->NPC_class == CLASS_ATST )
  1152. {
  1153. // FIXME: this is kind of dumb, but it was the easiest way to get it to look sort of ok
  1154. NPCInfo->desiredYaw += Q_flrand( -5, 5 ) + sin( level.time * 0.004f ) * 7;
  1155. NPCInfo->desiredPitch += Q_flrand( -2, 2 );
  1156. }
  1157. //Face that yaw
  1158. NPC_UpdateAngles( qtrue, qtrue );
  1159. //Find the delta between our goal and our current facing
  1160. float yawDelta = AngleNormalize360( NPCInfo->desiredYaw - ( SHORT2ANGLE( ucmd.angles[YAW] + client->ps.delta_angles[YAW] ) ) );
  1161. //See if we are facing properly
  1162. if ( fabs( yawDelta ) > VALID_ATTACK_CONE )
  1163. facing = qfalse;
  1164. if ( doPitch )
  1165. {
  1166. //Find the delta between our goal and our current facing
  1167. float currentAngles = ( SHORT2ANGLE( ucmd.angles[PITCH] + client->ps.delta_angles[PITCH] ) );
  1168. float pitchDelta = NPCInfo->desiredPitch - currentAngles;
  1169. //See if we are facing properly
  1170. if ( fabs( pitchDelta ) > VALID_ATTACK_CONE )
  1171. facing = qfalse;
  1172. }
  1173. return facing;
  1174. }
  1175. /*
  1176. -------------------------
  1177. NPC_FaceEntity
  1178. -------------------------
  1179. */
  1180. qboolean NPC_FaceEntity( gentity_t *ent, qboolean doPitch )
  1181. {
  1182. vec3_t entPos;
  1183. //Get the positions
  1184. CalcEntitySpot( ent, SPOT_HEAD_LEAN, entPos );
  1185. return NPC_FacePosition( entPos, doPitch );
  1186. }
  1187. /*
  1188. -------------------------
  1189. NPC_FaceEnemy
  1190. -------------------------
  1191. */
  1192. qboolean NPC_FaceEnemy( qboolean doPitch )
  1193. {
  1194. if ( NPC == NULL )
  1195. return qfalse;
  1196. if ( NPC->enemy == NULL )
  1197. return qfalse;
  1198. return NPC_FaceEntity( NPC->enemy, doPitch );
  1199. }
  1200. /*
  1201. -------------------------
  1202. NPC_CheckCanAttackExt
  1203. -------------------------
  1204. */
  1205. qboolean NPC_CheckCanAttackExt( void )
  1206. {
  1207. //We don't want them to shoot
  1208. if( NPCInfo->scriptFlags & SCF_DONT_FIRE )
  1209. return qfalse;
  1210. //Turn to face
  1211. if ( NPC_FaceEnemy( qtrue ) == qfalse )
  1212. return qfalse;
  1213. //Must have a clear line of sight to the target
  1214. if ( NPC_ClearShot( NPC->enemy ) == qfalse )
  1215. return qfalse;
  1216. return qtrue;
  1217. }
  1218. /*
  1219. -------------------------
  1220. NPC_ClearLookTarget
  1221. -------------------------
  1222. */
  1223. void NPC_ClearLookTarget( gentity_t *self )
  1224. {
  1225. if ( !self->client )
  1226. {
  1227. return;
  1228. }
  1229. self->client->renderInfo.lookTarget = ENTITYNUM_NONE;//ENTITYNUM_WORLD;
  1230. self->client->renderInfo.lookTargetClearTime = 0;
  1231. }
  1232. /*
  1233. -------------------------
  1234. NPC_SetLookTarget
  1235. -------------------------
  1236. */
  1237. void NPC_SetLookTarget( gentity_t *self, int entNum, int clearTime )
  1238. {
  1239. if ( !self->client )
  1240. {
  1241. return;
  1242. }
  1243. self->client->renderInfo.lookTarget = entNum;
  1244. self->client->renderInfo.lookTargetClearTime = clearTime;
  1245. }
  1246. /*
  1247. -------------------------
  1248. NPC_CheckLookTarget
  1249. -------------------------
  1250. */
  1251. qboolean NPC_CheckLookTarget( gentity_t *self )
  1252. {
  1253. if ( self->client )
  1254. {
  1255. if ( self->client->renderInfo.lookTarget >= 0 && self->client->renderInfo.lookTarget < ENTITYNUM_WORLD )
  1256. {//within valid range
  1257. if ( (&g_entities[self->client->renderInfo.lookTarget] == NULL) || !g_entities[self->client->renderInfo.lookTarget].inuse )
  1258. {//lookTarget not inuse or not valid anymore
  1259. NPC_ClearLookTarget( self );
  1260. }
  1261. else if ( self->client->renderInfo.lookTargetClearTime && self->client->renderInfo.lookTargetClearTime < level.time )
  1262. {//Time to clear lookTarget
  1263. NPC_ClearLookTarget( self );
  1264. }
  1265. else if ( g_entities[self->client->renderInfo.lookTarget].client && self->enemy && (&g_entities[self->client->renderInfo.lookTarget] != self->enemy) )
  1266. {//should always look at current enemy if engaged in battle... FIXME: this could override certain scripted lookTargets...???
  1267. NPC_ClearLookTarget( self );
  1268. }
  1269. else
  1270. {
  1271. return qtrue;
  1272. }
  1273. }
  1274. }
  1275. return qfalse;
  1276. }
  1277. /*
  1278. -------------------------
  1279. NPC_CheckCharmed
  1280. -------------------------
  1281. */
  1282. extern void G_AddVoiceEvent( gentity_t *self, int event, int speakDebounceTime );
  1283. void G_CheckCharmed( gentity_t *self )
  1284. {
  1285. if ( self
  1286. && self->client
  1287. && self->client->playerTeam == TEAM_PLAYER
  1288. && self->NPC
  1289. && self->NPC->charmedTime
  1290. && (self->NPC->charmedTime < level.time ||self->health <= 0) )
  1291. {//we were charmed, set us back!
  1292. //NOTE: presumptions here...
  1293. team_t savTeam = self->client->enemyTeam;
  1294. self->client->enemyTeam = self->client->playerTeam;
  1295. self->client->playerTeam = savTeam;
  1296. self->client->leader = NULL;
  1297. self->NPC->charmedTime = 0;
  1298. if ( self->health > 0 )
  1299. {
  1300. if ( self->NPC->tempBehavior == BS_FOLLOW_LEADER )
  1301. {
  1302. self->NPC->tempBehavior = BS_DEFAULT;
  1303. }
  1304. G_ClearEnemy( self );
  1305. //say something to let player know you've snapped out of it
  1306. G_AddVoiceEvent( self, Q_irand(EV_CONFUSE1, EV_CONFUSE3), 2000 );
  1307. }
  1308. }
  1309. }
  1310. void G_GetBoltPosition( gentity_t *self, int boltIndex, vec3_t pos, int modelIndex = 0 )
  1311. {
  1312. if ( !self || !self->ghoul2.size() )
  1313. {
  1314. return;
  1315. }
  1316. mdxaBone_t boltMatrix;
  1317. vec3_t result, angles={0,self->currentAngles[YAW],0};
  1318. gi.G2API_GetBoltMatrix( self->ghoul2, modelIndex,
  1319. boltIndex,
  1320. &boltMatrix, angles, self->currentOrigin, (cg.time?cg.time:level.time),
  1321. NULL, self->s.modelScale );
  1322. if ( pos )
  1323. {
  1324. gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, result );
  1325. VectorCopy( result, pos );
  1326. }
  1327. }
  1328. float NPC_EntRangeFromBolt( gentity_t *targEnt, int boltIndex )
  1329. {
  1330. vec3_t org;
  1331. if ( !targEnt )
  1332. {
  1333. return Q3_INFINITE;
  1334. }
  1335. G_GetBoltPosition( NPC, boltIndex, org );
  1336. return (Distance( targEnt->currentOrigin, org ));
  1337. }
  1338. float NPC_EnemyRangeFromBolt( int boltIndex )
  1339. {
  1340. return (NPC_EntRangeFromBolt( NPC->enemy, boltIndex ));
  1341. }
  1342. int G_GetEntsNearBolt( gentity_t *self, gentity_t **radiusEnts, float radius, int boltIndex, vec3_t boltOrg )
  1343. {
  1344. vec3_t mins, maxs;
  1345. int i;
  1346. //get my handRBolt's position
  1347. vec3_t org;
  1348. G_GetBoltPosition( self, boltIndex, org );
  1349. VectorCopy( org, boltOrg );
  1350. //Setup the bbox to search in
  1351. for ( i = 0; i < 3; i++ )
  1352. {
  1353. mins[i] = boltOrg[i] - radius;
  1354. maxs[i] = boltOrg[i] + radius;
  1355. }
  1356. //Get the number of entities in a given space
  1357. return (gi.EntitiesInBox( mins, maxs, radiusEnts, 128 ));
  1358. }
  1359. int NPC_GetEntsNearBolt( gentity_t **radiusEnts, float radius, int boltIndex, vec3_t boltOrg )
  1360. {
  1361. return (G_GetEntsNearBolt( NPC, radiusEnts, radius, boltIndex, boltOrg ));
  1362. }
  1363. extern qboolean RT_Flying( gentity_t *self );
  1364. extern void RT_FlyStart( gentity_t *self );
  1365. extern void RT_FlyStop( gentity_t *self );
  1366. extern qboolean Boba_Flying( gentity_t *self );
  1367. extern void Boba_FlyStart( gentity_t *self );
  1368. extern void Boba_FlyStop( gentity_t *self );
  1369. qboolean JET_Flying( gentity_t *self )
  1370. {
  1371. if ( !self || !self->client )
  1372. {
  1373. return qfalse;
  1374. }
  1375. if ( self->client->NPC_class == CLASS_BOBAFETT )
  1376. {
  1377. return (Boba_Flying(self));
  1378. }
  1379. else if ( self->client->NPC_class == CLASS_ROCKETTROOPER )
  1380. {
  1381. return (RT_Flying(self));
  1382. }
  1383. else
  1384. {
  1385. return qfalse;
  1386. }
  1387. }
  1388. void JET_FlyStart( gentity_t *self )
  1389. {
  1390. if ( !self || !self->client )
  1391. {
  1392. return;
  1393. }
  1394. self->lastInAirTime = level.time;
  1395. if ( self->client->NPC_class == CLASS_BOBAFETT )
  1396. {
  1397. Boba_FlyStart( self );
  1398. }
  1399. else if ( self->client->NPC_class == CLASS_ROCKETTROOPER )
  1400. {
  1401. RT_FlyStart( self );
  1402. }
  1403. }
  1404. void JET_FlyStop( gentity_t *self )
  1405. {
  1406. if ( !self || !self->client )
  1407. {
  1408. return;
  1409. }
  1410. if ( self->client->NPC_class == CLASS_BOBAFETT )
  1411. {
  1412. Boba_FlyStop( self );
  1413. }
  1414. else if ( self->client->NPC_class == CLASS_ROCKETTROOPER )
  1415. {
  1416. RT_FlyStop( self );
  1417. }
  1418. }