PageRenderTime 83ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 1ms

/codemp/game/w_force.c

https://github.com/Stoiss/jaMME
C | 5792 lines | 4893 code | 677 blank | 222 comment | 1442 complexity | 5c05792ec0909128ed6bb1696fe073f0 MD5 | raw file
Possible License(s): GPL-2.0
  1. #include "b_local.h"
  2. #include "w_saber.h"
  3. #include "ai_main.h"
  4. #include "ghoul2/G2.h"
  5. #define METROID_JUMP 1
  6. //NEEDED FOR MIND-TRICK on NPCS=========================================================
  7. extern void NPC_PlayConfusionSound( gentity_t *self );
  8. extern void NPC_Jedi_PlayConfusionSound( gentity_t *self );
  9. extern void NPC_UseResponse( gentity_t *self, gentity_t *user, qboolean useWhenDone );
  10. //NEEDED FOR MIND-TRICK on NPCS=========================================================
  11. extern void Jedi_Decloak( gentity_t *self );
  12. extern qboolean BG_FullBodyTauntAnim( int anim );
  13. extern bot_state_t *botstates[MAX_CLIENTS];
  14. int speedLoopSound = 0;
  15. int rageLoopSound = 0;
  16. int protectLoopSound = 0;
  17. int absorbLoopSound = 0;
  18. int seeLoopSound = 0;
  19. int ysalamiriLoopSound = 0;
  20. #define FORCE_VELOCITY_DAMAGE 0
  21. int ForceShootDrain( gentity_t *self );
  22. gentity_t *G_PreDefSound(vec3_t org, int pdSound)
  23. {
  24. gentity_t *te;
  25. te = G_TempEntity( org, EV_PREDEFSOUND );
  26. te->s.eventParm = pdSound;
  27. VectorCopy(org, te->s.origin);
  28. return te;
  29. }
  30. const int forcePowerMinRank[NUM_FORCE_POWER_LEVELS][NUM_FORCE_POWERS] = //0 == neutral
  31. {
  32. {
  33. 999,//FP_HEAL,//instant
  34. 999,//FP_LEVITATION,//hold/duration
  35. 999,//FP_SPEED,//duration
  36. 999,//FP_PUSH,//hold/duration
  37. 999,//FP_PULL,//hold/duration
  38. 999,//FP_TELEPATHY,//instant
  39. 999,//FP_GRIP,//hold/duration
  40. 999,//FP_LIGHTNING,//hold/duration
  41. 999,//FP_RAGE,//duration
  42. 999,//FP_PROTECT,//duration
  43. 999,//FP_ABSORB,//duration
  44. 999,//FP_TEAM_HEAL,//instant
  45. 999,//FP_TEAM_FORCE,//instant
  46. 999,//FP_DRAIN,//hold/duration
  47. 999,//FP_SEE,//duration
  48. 999,//FP_SABER_OFFENSE,
  49. 999,//FP_SABER_DEFENSE,
  50. 999//FP_SABERTHROW,
  51. //NUM_FORCE_POWERS
  52. },
  53. {
  54. 10,//FP_HEAL,//instant
  55. 0,//FP_LEVITATION,//hold/duration
  56. 0,//FP_SPEED,//duration
  57. 0,//FP_PUSH,//hold/duration
  58. 0,//FP_PULL,//hold/duration
  59. 10,//FP_TELEPATHY,//instant
  60. 15,//FP_GRIP,//hold/duration
  61. 10,//FP_LIGHTNING,//hold/duration
  62. 15,//FP_RAGE,//duration
  63. 15,//FP_PROTECT,//duration
  64. 15,//FP_ABSORB,//duration
  65. 10,//FP_TEAM_HEAL,//instant
  66. 10,//FP_TEAM_FORCE,//instant
  67. 10,//FP_DRAIN,//hold/duration
  68. 5,//FP_SEE,//duration
  69. 0,//FP_SABER_OFFENSE,
  70. 0,//FP_SABER_DEFENSE,
  71. 0//FP_SABERTHROW,
  72. //NUM_FORCE_POWERS
  73. },
  74. {
  75. 10,//FP_HEAL,//instant
  76. 0,//FP_LEVITATION,//hold/duration
  77. 0,//FP_SPEED,//duration
  78. 0,//FP_PUSH,//hold/duration
  79. 0,//FP_PULL,//hold/duration
  80. 10,//FP_TELEPATHY,//instant
  81. 15,//FP_GRIP,//hold/duration
  82. 10,//FP_LIGHTNING,//hold/duration
  83. 15,//FP_RAGE,//duration
  84. 15,//FP_PROTECT,//duration
  85. 15,//FP_ABSORB,//duration
  86. 10,//FP_TEAM_HEAL,//instant
  87. 10,//FP_TEAM_FORCE,//instant
  88. 10,//FP_DRAIN,//hold/duration
  89. 5,//FP_SEE,//duration
  90. 5,//FP_SABER_OFFENSE,
  91. 5,//FP_SABER_DEFENSE,
  92. 5//FP_SABERTHROW,
  93. //NUM_FORCE_POWERS
  94. },
  95. {
  96. 10,//FP_HEAL,//instant
  97. 0,//FP_LEVITATION,//hold/duration
  98. 0,//FP_SPEED,//duration
  99. 0,//FP_PUSH,//hold/duration
  100. 0,//FP_PULL,//hold/duration
  101. 10,//FP_TELEPATHY,//instant
  102. 15,//FP_GRIP,//hold/duration
  103. 10,//FP_LIGHTNING,//hold/duration
  104. 15,//FP_RAGE,//duration
  105. 15,//FP_PROTECT,//duration
  106. 15,//FP_ABSORB,//duration
  107. 10,//FP_TEAM_HEAL,//instant
  108. 10,//FP_TEAM_FORCE,//instant
  109. 10,//FP_DRAIN,//hold/duration
  110. 5,//FP_SEE,//duration
  111. 10,//FP_SABER_OFFENSE,
  112. 10,//FP_SABER_DEFENSE,
  113. 10//FP_SABERTHROW,
  114. //NUM_FORCE_POWERS
  115. }
  116. };
  117. const int mindTrickTime[NUM_FORCE_POWER_LEVELS] =
  118. {
  119. 0,//none
  120. 5000,
  121. 10000,
  122. 15000
  123. };
  124. void WP_InitForcePowers( gentity_t *ent )
  125. {
  126. int i;
  127. int i_r;
  128. int maxRank = g_maxForceRank.integer;
  129. qboolean warnClient = qfalse;
  130. qboolean warnClientLimit = qfalse;
  131. char userinfo[MAX_INFO_STRING];
  132. char forcePowers[256];
  133. char readBuf[256];
  134. int lastFPKnown = -1;
  135. qboolean didEvent = qfalse;
  136. if (!maxRank)
  137. { //if server has no max rank, default to max (50)
  138. maxRank = FORCE_MASTERY_JEDI_MASTER;
  139. }
  140. else if (maxRank >= NUM_FORCE_MASTERY_LEVELS)
  141. {//ack, prevent user from being dumb
  142. maxRank = FORCE_MASTERY_JEDI_MASTER;
  143. trap_Cvar_Set( "g_maxForceRank", va("%i", maxRank) );
  144. }
  145. /*
  146. if (g_forcePowerDisable.integer)
  147. {
  148. maxRank = FORCE_MASTERY_UNINITIATED;
  149. }
  150. */
  151. //rww - don't do this
  152. if ( !ent || !ent->client )
  153. {
  154. return;
  155. }
  156. ent->client->ps.fd.saberAnimLevel = ent->client->sess.saberLevel;
  157. if (ent->client->ps.fd.saberAnimLevel < FORCE_LEVEL_1 ||
  158. ent->client->ps.fd.saberAnimLevel > FORCE_LEVEL_3)
  159. {
  160. ent->client->ps.fd.saberAnimLevel = FORCE_LEVEL_1;
  161. }
  162. if (!speedLoopSound)
  163. { //so that the client configstring is already modified with this when we need it
  164. speedLoopSound = G_SoundIndex("sound/weapons/force/speedloop.wav");
  165. }
  166. if (!rageLoopSound)
  167. {
  168. rageLoopSound = G_SoundIndex("sound/weapons/force/rageloop.wav");
  169. }
  170. if (!absorbLoopSound)
  171. {
  172. absorbLoopSound = G_SoundIndex("sound/weapons/force/absorbloop.wav");
  173. }
  174. if (!protectLoopSound)
  175. {
  176. protectLoopSound = G_SoundIndex("sound/weapons/force/protectloop.wav");
  177. }
  178. if (!seeLoopSound)
  179. {
  180. seeLoopSound = G_SoundIndex("sound/weapons/force/seeloop.wav");
  181. }
  182. if (!ysalamiriLoopSound)
  183. {
  184. ysalamiriLoopSound = G_SoundIndex("sound/player/nullifyloop.wav");
  185. }
  186. if (ent->s.eType == ET_NPC)
  187. { //just stop here then.
  188. return;
  189. }
  190. i = 0;
  191. while (i < NUM_FORCE_POWERS)
  192. {
  193. ent->client->ps.fd.forcePowerLevel[i] = 0;
  194. ent->client->ps.fd.forcePowersKnown &= ~(1 << i);
  195. i++;
  196. }
  197. ent->client->ps.fd.forcePowerSelected = -1;
  198. ent->client->ps.fd.forceSide = 0;
  199. if (level.gametype == GT_SIEGE &&
  200. ent->client->siegeClass != -1)
  201. { //Then use the powers for this class, and skip all this nonsense.
  202. i = 0;
  203. while (i < NUM_FORCE_POWERS)
  204. {
  205. ent->client->ps.fd.forcePowerLevel[i] = bgSiegeClasses[ent->client->siegeClass].forcePowerLevels[i];
  206. if (!ent->client->ps.fd.forcePowerLevel[i])
  207. {
  208. ent->client->ps.fd.forcePowersKnown &= ~(1 << i);
  209. }
  210. else
  211. {
  212. ent->client->ps.fd.forcePowersKnown |= (1 << i);
  213. }
  214. i++;
  215. }
  216. if (!ent->client->sess.setForce)
  217. {
  218. //bring up the class selection menu
  219. trap_SendServerCommand(ent-g_entities, "scl");
  220. }
  221. ent->client->sess.setForce = qtrue;
  222. return;
  223. }
  224. if (ent->s.eType == ET_NPC && ent->s.number >= MAX_CLIENTS)
  225. { //rwwFIXMEFIXME: Temp
  226. Q_strncpyz( userinfo, "forcepowers\\7-1-333003000313003120", sizeof( userinfo ) );
  227. }
  228. else
  229. {
  230. trap_GetUserinfo( ent->s.number, userinfo, sizeof( userinfo ) );
  231. }
  232. Q_strncpyz( forcePowers, Info_ValueForKey( userinfo, "forcepowers" ), sizeof( forcePowers ) );
  233. if ( strlen( forcePowers ) < strlen( DEFAULT_FORCEPOWERS ) )
  234. {
  235. Q_strncpyz( forcePowers, DEFAULT_FORCEPOWERS, sizeof( forcePowers ) );
  236. trap_SendServerCommand( ent-g_entities, "print \"^1Invalid forcepowers string, setting default\n\"" );
  237. }
  238. //if it's a bot just copy the info directly from its personality
  239. if ( (ent->r.svFlags & SVF_BOT) && botstates[ent->s.number] )
  240. Q_strncpyz( forcePowers, botstates[ent->s.number]->forceinfo, sizeof( forcePowers ) );
  241. //rww - parse through the string manually and eat out all the appropriate data
  242. i = 0;
  243. if (g_forceBasedTeams.integer)
  244. {
  245. if (ent->client->sess.sessionTeam == TEAM_RED)
  246. {
  247. warnClient = !(BG_LegalizedForcePowers(forcePowers, maxRank, HasSetSaberOnly(), FORCE_DARKSIDE, level.gametype, g_forcePowerDisable.integer));
  248. }
  249. else if (ent->client->sess.sessionTeam == TEAM_BLUE)
  250. {
  251. warnClient = !(BG_LegalizedForcePowers(forcePowers, maxRank, HasSetSaberOnly(), FORCE_LIGHTSIDE, level.gametype, g_forcePowerDisable.integer));
  252. }
  253. else
  254. {
  255. warnClient = !(BG_LegalizedForcePowers(forcePowers, maxRank, HasSetSaberOnly(), 0, level.gametype, g_forcePowerDisable.integer));
  256. }
  257. }
  258. else
  259. {
  260. warnClient = !(BG_LegalizedForcePowers(forcePowers, maxRank, HasSetSaberOnly(), 0, level.gametype, g_forcePowerDisable.integer));
  261. }
  262. i_r = 0;
  263. while (forcePowers[i] && forcePowers[i] != '-')
  264. {
  265. readBuf[i_r] = forcePowers[i];
  266. i_r++;
  267. i++;
  268. }
  269. readBuf[i_r] = 0;
  270. //THE RANK
  271. ent->client->ps.fd.forceRank = atoi(readBuf);
  272. i++;
  273. i_r = 0;
  274. while (forcePowers[i] && forcePowers[i] != '-')
  275. {
  276. readBuf[i_r] = forcePowers[i];
  277. i_r++;
  278. i++;
  279. }
  280. readBuf[i_r] = 0;
  281. //THE SIDE
  282. ent->client->ps.fd.forceSide = atoi(readBuf);
  283. i++;
  284. if ( level.gametype != GT_SIEGE && (ent->r.svFlags & SVF_BOT) && botstates[ent->s.number] )
  285. { //hmm..I'm going to cheat here.
  286. int oldI = i;
  287. i_r = 0;
  288. while (forcePowers[i] && forcePowers[i] != '\n' &&
  289. i_r < NUM_FORCE_POWERS)
  290. {
  291. if (ent->client->ps.fd.forceSide == FORCE_LIGHTSIDE)
  292. {
  293. if (i_r == FP_ABSORB)
  294. {
  295. forcePowers[i] = '3';
  296. }
  297. if (botstates[ent->s.number]->settings.skill >= 4)
  298. { //cheat and give them more stuff
  299. if (i_r == FP_HEAL)
  300. {
  301. forcePowers[i] = '3';
  302. }
  303. else if (i_r == FP_PROTECT)
  304. {
  305. forcePowers[i] = '3';
  306. }
  307. }
  308. }
  309. else if (ent->client->ps.fd.forceSide == FORCE_DARKSIDE)
  310. {
  311. if (botstates[ent->s.number]->settings.skill >= 4)
  312. {
  313. if (i_r == FP_GRIP)
  314. {
  315. forcePowers[i] = '3';
  316. }
  317. else if (i_r == FP_LIGHTNING)
  318. {
  319. forcePowers[i] = '3';
  320. }
  321. else if (i_r == FP_RAGE)
  322. {
  323. forcePowers[i] = '3';
  324. }
  325. else if (i_r == FP_DRAIN)
  326. {
  327. forcePowers[i] = '3';
  328. }
  329. }
  330. }
  331. if (i_r == FP_PUSH)
  332. {
  333. forcePowers[i] = '3';
  334. }
  335. else if (i_r == FP_PULL)
  336. {
  337. forcePowers[i] = '3';
  338. }
  339. i++;
  340. i_r++;
  341. }
  342. i = oldI;
  343. }
  344. i_r = 0;
  345. while (forcePowers[i] && forcePowers[i] != '\n' &&
  346. i_r < NUM_FORCE_POWERS)
  347. {
  348. readBuf[0] = forcePowers[i];
  349. readBuf[1] = 0;
  350. ent->client->ps.fd.forcePowerLevel[i_r] = atoi(readBuf);
  351. if (ent->client->ps.fd.forcePowerLevel[i_r])
  352. {
  353. ent->client->ps.fd.forcePowersKnown |= (1 << i_r);
  354. }
  355. else
  356. {
  357. ent->client->ps.fd.forcePowersKnown &= ~(1 << i_r);
  358. }
  359. i++;
  360. i_r++;
  361. }
  362. //THE POWERS
  363. if (ent->s.eType != ET_NPC)
  364. {
  365. if (HasSetSaberOnly())
  366. {
  367. gentity_t *te = G_TempEntity( vec3_origin, EV_SET_FREE_SABER );
  368. te->r.svFlags |= SVF_BROADCAST;
  369. te->s.eventParm = 1;
  370. }
  371. else
  372. {
  373. gentity_t *te = G_TempEntity( vec3_origin, EV_SET_FREE_SABER );
  374. te->r.svFlags |= SVF_BROADCAST;
  375. te->s.eventParm = 0;
  376. }
  377. if (g_forcePowerDisable.integer)
  378. {
  379. gentity_t *te = G_TempEntity( vec3_origin, EV_SET_FORCE_DISABLE );
  380. te->r.svFlags |= SVF_BROADCAST;
  381. te->s.eventParm = 1;
  382. }
  383. else
  384. {
  385. gentity_t *te = G_TempEntity( vec3_origin, EV_SET_FORCE_DISABLE );
  386. te->r.svFlags |= SVF_BROADCAST;
  387. te->s.eventParm = 0;
  388. }
  389. }
  390. //rww - It seems we currently want to always do this, even if the player isn't exceeding the max
  391. //rank, so..
  392. // if (level.gametype == GT_DUEL || level.gametype == GT_POWERDUEL)
  393. // { //totally messes duel up to force someone into spec mode, and besides, each "round" is
  394. //counted as a full restart
  395. // ent->client->sess.setForce = qtrue;
  396. // }
  397. if (ent->s.eType == ET_NPC)
  398. {
  399. ent->client->sess.setForce = qtrue;
  400. }
  401. else if (level.gametype == GT_SIEGE)
  402. {
  403. if (!ent->client->sess.setForce)
  404. {
  405. ent->client->sess.setForce = qtrue;
  406. //bring up the class selection menu
  407. trap_SendServerCommand(ent-g_entities, "scl");
  408. }
  409. }
  410. else
  411. {
  412. if (warnClient || !ent->client->sess.setForce)
  413. { //the client's rank is too high for the server and has been autocapped, so tell them
  414. if (level.gametype != GT_HOLOCRON && level.gametype != GT_JEDIMASTER )
  415. {
  416. #ifdef EVENT_FORCE_RANK
  417. gentity_t *te = G_TempEntity( vec3_origin, EV_GIVE_NEW_RANK );
  418. te->r.svFlags |= SVF_BROADCAST;
  419. te->s.trickedentindex = ent->s.number;
  420. te->s.eventParm = maxRank;
  421. te->s.bolt1 = 0;
  422. #endif
  423. didEvent = qtrue;
  424. // if (!(ent->r.svFlags & SVF_BOT) && level.gametype != GT_DUEL && level.gametype != GT_POWERDUEL && ent->s.eType != ET_NPC)
  425. if (!(ent->r.svFlags & SVF_BOT) && ent->s.eType != ET_NPC)
  426. {
  427. if (!g_teamAutoJoin.integer)
  428. {
  429. //Make them a spectator so they can set their powerups up without being bothered.
  430. ent->client->sess.sessionTeam = TEAM_SPECTATOR;
  431. ent->client->sess.spectatorState = SPECTATOR_FREE;
  432. ent->client->sess.spectatorClient = 0;
  433. ent->client->pers.teamState.state = TEAM_BEGIN;
  434. trap_SendServerCommand(ent-g_entities, "spc"); // Fire up the profile menu
  435. }
  436. }
  437. #ifdef EVENT_FORCE_RANK
  438. te->s.bolt2 = ent->client->sess.sessionTeam;
  439. #else
  440. //Event isn't very reliable, I made it a string. This way I can send it to just one
  441. //client also, as opposed to making a broadcast event.
  442. trap_SendServerCommand(ent->s.number, va("nfr %i %i %i", maxRank, 1, ent->client->sess.sessionTeam));
  443. //Arg1 is new max rank, arg2 is non-0 if force menu should be shown, arg3 is the current team
  444. #endif
  445. }
  446. ent->client->sess.setForce = qtrue;
  447. }
  448. if (!didEvent )
  449. {
  450. #ifdef EVENT_FORCE_RANK
  451. gentity_t *te = G_TempEntity( vec3_origin, EV_GIVE_NEW_RANK );
  452. te->r.svFlags |= SVF_BROADCAST;
  453. te->s.trickedentindex = ent->s.number;
  454. te->s.eventParm = maxRank;
  455. te->s.bolt1 = 1;
  456. te->s.bolt2 = ent->client->sess.sessionTeam;
  457. #else
  458. trap_SendServerCommand(ent->s.number, va("nfr %i %i %i", maxRank, 0, ent->client->sess.sessionTeam));
  459. #endif
  460. }
  461. if (warnClientLimit)
  462. { //the server has one or more force powers disabled and the client is using them in his config
  463. //trap_SendServerCommand(ent-g_entities, va("print \"The server has one or more force powers that you have chosen disabled.\nYou will not be able to use the disable force power(s) while playing on this server.\n\""));
  464. }
  465. }
  466. i = 0;
  467. while (i < NUM_FORCE_POWERS)
  468. {
  469. if ((ent->client->ps.fd.forcePowersKnown & (1 << i)) &&
  470. !ent->client->ps.fd.forcePowerLevel[i])
  471. { //err..
  472. ent->client->ps.fd.forcePowersKnown &= ~(1 << i);
  473. }
  474. else
  475. {
  476. if (i != FP_LEVITATION && i != FP_SABER_OFFENSE && i != FP_SABER_DEFENSE && i != FP_SABERTHROW)
  477. {
  478. lastFPKnown = i;
  479. }
  480. }
  481. i++;
  482. }
  483. if (ent->client->ps.fd.forcePowersKnown & ent->client->sess.selectedFP)
  484. {
  485. ent->client->ps.fd.forcePowerSelected = ent->client->sess.selectedFP;
  486. }
  487. if (!(ent->client->ps.fd.forcePowersKnown & (1 << ent->client->ps.fd.forcePowerSelected)))
  488. {
  489. if (lastFPKnown != -1)
  490. {
  491. ent->client->ps.fd.forcePowerSelected = lastFPKnown;
  492. }
  493. else
  494. {
  495. ent->client->ps.fd.forcePowerSelected = 0;
  496. }
  497. }
  498. while (i < NUM_FORCE_POWERS)
  499. {
  500. ent->client->ps.fd.forcePowerBaseLevel[i] = ent->client->ps.fd.forcePowerLevel[i];
  501. i++;
  502. }
  503. ent->client->ps.fd.forceUsingAdded = 0;
  504. }
  505. void WP_SpawnInitForcePowers( gentity_t *ent )
  506. {
  507. int i = 0;
  508. ent->client->ps.saberAttackChainCount = 0;
  509. i = 0;
  510. while (i < NUM_FORCE_POWERS)
  511. {
  512. if (ent->client->ps.fd.forcePowersActive & (1 << i))
  513. {
  514. WP_ForcePowerStop(ent, i);
  515. }
  516. i++;
  517. }
  518. ent->client->ps.fd.forceDeactivateAll = 0;
  519. ent->client->ps.fd.forcePower = ent->client->ps.fd.forcePowerMax = FORCE_POWER_MAX;
  520. ent->client->ps.fd.forcePowerRegenDebounceTime = 0;
  521. ent->client->ps.fd.forceGripEntityNum = ENTITYNUM_NONE;
  522. ent->client->ps.fd.forceMindtrickTargetIndex = 0;
  523. ent->client->ps.fd.forceMindtrickTargetIndex2 = 0;
  524. ent->client->ps.fd.forceMindtrickTargetIndex3 = 0;
  525. ent->client->ps.fd.forceMindtrickTargetIndex4 = 0;
  526. ent->client->ps.holocronBits = 0;
  527. i = 0;
  528. while (i < NUM_FORCE_POWERS)
  529. {
  530. ent->client->ps.holocronsCarried[i] = 0;
  531. i++;
  532. }
  533. if (level.gametype == GT_HOLOCRON)
  534. {
  535. i = 0;
  536. while (i < NUM_FORCE_POWERS)
  537. {
  538. ent->client->ps.fd.forcePowerLevel[i] = FORCE_LEVEL_0;
  539. i++;
  540. }
  541. if (HasSetSaberOnly())
  542. {
  543. if (ent->client->ps.fd.forcePowerLevel[FP_SABER_OFFENSE] < FORCE_LEVEL_1)
  544. {
  545. ent->client->ps.fd.forcePowerLevel[FP_SABER_OFFENSE] = FORCE_LEVEL_1;
  546. }
  547. if (ent->client->ps.fd.forcePowerLevel[FP_SABER_DEFENSE] < FORCE_LEVEL_1)
  548. {
  549. ent->client->ps.fd.forcePowerLevel[FP_SABER_DEFENSE] = FORCE_LEVEL_1;
  550. }
  551. }
  552. }
  553. i = 0;
  554. while (i < NUM_FORCE_POWERS)
  555. {
  556. ent->client->ps.fd.forcePowerDebounce[i] = 0;
  557. ent->client->ps.fd.forcePowerDuration[i] = 0;
  558. i++;
  559. }
  560. ent->client->ps.fd.forcePowerRegenDebounceTime = 0;
  561. ent->client->ps.fd.forceJumpZStart = 0;
  562. ent->client->ps.fd.forceJumpCharge = 0;
  563. ent->client->ps.fd.forceJumpSound = 0;
  564. ent->client->ps.fd.forceGripDamageDebounceTime = 0;
  565. ent->client->ps.fd.forceGripBeingGripped = 0;
  566. ent->client->ps.fd.forceGripCripple = 0;
  567. ent->client->ps.fd.forceGripUseTime = 0;
  568. ent->client->ps.fd.forceGripSoundTime = 0;
  569. ent->client->ps.fd.forceGripStarted = 0;
  570. ent->client->ps.fd.forceHealTime = 0;
  571. ent->client->ps.fd.forceHealAmount = 0;
  572. ent->client->ps.fd.forceRageRecoveryTime = 0;
  573. ent->client->ps.fd.forceDrainEntNum = ENTITYNUM_NONE;
  574. ent->client->ps.fd.forceDrainTime = 0;
  575. i = 0;
  576. while (i < NUM_FORCE_POWERS)
  577. {
  578. if ((ent->client->ps.fd.forcePowersKnown & (1 << i)) &&
  579. !ent->client->ps.fd.forcePowerLevel[i])
  580. { //make sure all known powers are cleared if we have level 0 in them
  581. ent->client->ps.fd.forcePowersKnown &= ~(1 << i);
  582. }
  583. i++;
  584. }
  585. if (level.gametype == GT_SIEGE &&
  586. ent->client->siegeClass != -1)
  587. { //Then use the powers for this class.
  588. i = 0;
  589. while (i < NUM_FORCE_POWERS)
  590. {
  591. ent->client->ps.fd.forcePowerLevel[i] = bgSiegeClasses[ent->client->siegeClass].forcePowerLevels[i];
  592. if (!ent->client->ps.fd.forcePowerLevel[i])
  593. {
  594. ent->client->ps.fd.forcePowersKnown &= ~(1 << i);
  595. }
  596. else
  597. {
  598. ent->client->ps.fd.forcePowersKnown |= (1 << i);
  599. }
  600. i++;
  601. }
  602. }
  603. }
  604. extern qboolean BG_InKnockDown( int anim ); //bg_pmove.c
  605. int ForcePowerUsableOn(gentity_t *attacker, gentity_t *other, forcePowers_t forcePower)
  606. {
  607. if (other && other->client && BG_HasYsalamiri(level.gametype, &other->client->ps))
  608. {
  609. return 0;
  610. }
  611. if (attacker && attacker->client && !BG_CanUseFPNow(level.gametype, &attacker->client->ps, level.time, forcePower))
  612. {
  613. return 0;
  614. }
  615. //Dueling fighters cannot use force powers on others, with the exception of force push when locked with each other
  616. if (attacker && attacker->client && attacker->client->ps.duelInProgress)
  617. {
  618. return 0;
  619. }
  620. if (other && other->client && other->client->ps.duelInProgress)
  621. {
  622. return 0;
  623. }
  624. if (forcePower == FP_GRIP)
  625. {
  626. if (other && other->client &&
  627. (other->client->ps.fd.forcePowersActive & (1<<FP_ABSORB)))
  628. { //don't allow gripping to begin with if they are absorbing
  629. //play sound indicating that attack was absorbed
  630. if (other->client->forcePowerSoundDebounce < level.time)
  631. {
  632. gentity_t *abSound = G_PreDefSound(other->client->ps.origin, PDSOUND_ABSORBHIT);
  633. abSound->s.trickedentindex = other->s.number;
  634. other->client->forcePowerSoundDebounce = level.time + 400;
  635. }
  636. return 0;
  637. }
  638. else if (other && other->client &&
  639. other->client->ps.weapon == WP_SABER &&
  640. BG_SaberInSpecial(other->client->ps.saberMove))
  641. { //don't grip person while they are in a special or some really bad things can happen.
  642. return 0;
  643. }
  644. }
  645. if (other && other->client &&
  646. (forcePower == FP_PUSH ||
  647. forcePower == FP_PULL))
  648. {
  649. if (BG_InKnockDown(other->client->ps.legsAnim))
  650. {
  651. return 0;
  652. }
  653. }
  654. if (other && other->client && other->s.eType == ET_NPC &&
  655. other->s.NPC_class == CLASS_VEHICLE)
  656. { //can't use the force on vehicles.. except lightning
  657. if (forcePower == FP_LIGHTNING)
  658. {
  659. return 1;
  660. }
  661. else
  662. {
  663. return 0;
  664. }
  665. }
  666. if (other && other->client && other->s.eType == ET_NPC &&
  667. level.gametype == GT_SIEGE)
  668. { //can't use powers at all on npc's normally in siege...
  669. return 0;
  670. }
  671. return 1;
  672. }
  673. qboolean WP_ForcePowerAvailable( gentity_t *self, forcePowers_t forcePower, int overrideAmt )
  674. {
  675. int drain = overrideAmt ? overrideAmt :
  676. forcePowerNeeded[self->client->ps.fd.forcePowerLevel[forcePower]][forcePower];
  677. if (self->client->ps.fd.forcePowersActive & (1 << forcePower))
  678. { //we're probably going to deactivate it..
  679. return qtrue;
  680. }
  681. if ( forcePower == FP_LEVITATION )
  682. {
  683. return qtrue;
  684. }
  685. if ( !drain )
  686. {
  687. return qtrue;
  688. }
  689. if ((forcePower == FP_DRAIN || forcePower == FP_LIGHTNING) &&
  690. self->client->ps.fd.forcePower >= 25)
  691. { //it's ok then, drain/lightning are actually duration
  692. return qtrue;
  693. }
  694. if ( self->client->ps.fd.forcePower < drain )
  695. {
  696. return qfalse;
  697. }
  698. return qtrue;
  699. }
  700. qboolean WP_ForcePowerInUse( gentity_t *self, forcePowers_t forcePower )
  701. {
  702. if ( (self->client->ps.fd.forcePowersActive & ( 1 << forcePower )) )
  703. {//already using this power
  704. return qtrue;
  705. }
  706. return qfalse;
  707. }
  708. qboolean WP_ForcePowerUsable( gentity_t *self, forcePowers_t forcePower )
  709. {
  710. if (BG_HasYsalamiri(level.gametype, &self->client->ps))
  711. {
  712. return qfalse;
  713. }
  714. if (self->health <= 0 || self->client->ps.stats[STAT_HEALTH] <= 0 ||
  715. (self->client->ps.eFlags & EF_DEAD))
  716. {
  717. return qfalse;
  718. }
  719. if (self->client->ps.pm_flags & PMF_FOLLOW)
  720. { //specs can't use powers through people
  721. return qfalse;
  722. }
  723. if (self->client->sess.sessionTeam == TEAM_SPECTATOR)
  724. {
  725. return qfalse;
  726. }
  727. if (self->client->tempSpectate >= level.time)
  728. {
  729. return qfalse;
  730. }
  731. if (!BG_CanUseFPNow(level.gametype, &self->client->ps, level.time, forcePower))
  732. {
  733. return qfalse;
  734. }
  735. if ( !(self->client->ps.fd.forcePowersKnown & ( 1 << forcePower )) )
  736. {//don't know this power
  737. return qfalse;
  738. }
  739. if ( (self->client->ps.fd.forcePowersActive & ( 1 << forcePower )) )
  740. {//already using this power
  741. if (forcePower != FP_LEVITATION)
  742. {
  743. return qfalse;
  744. }
  745. }
  746. if (forcePower == FP_LEVITATION && self->client->fjDidJump)
  747. {
  748. return qfalse;
  749. }
  750. if (!self->client->ps.fd.forcePowerLevel[forcePower])
  751. {
  752. return qfalse;
  753. }
  754. if ( g_debugMelee.integer )
  755. {
  756. if ( (self->client->ps.pm_flags&PMF_STUCK_TO_WALL) )
  757. {//no offensive force powers when stuck to wall
  758. switch ( forcePower )
  759. {
  760. case FP_GRIP:
  761. case FP_LIGHTNING:
  762. case FP_DRAIN:
  763. case FP_SABER_OFFENSE:
  764. case FP_SABER_DEFENSE:
  765. case FP_SABERTHROW:
  766. return qfalse;
  767. break;
  768. default:
  769. break;
  770. }
  771. }
  772. }
  773. if ( !self->client->ps.saberHolstered )
  774. {
  775. if ( (self->client->saber[0].saberFlags&SFL_TWO_HANDED) )
  776. {
  777. if ( g_saberRestrictForce.integer )
  778. {
  779. switch ( forcePower )
  780. {
  781. case FP_PUSH:
  782. case FP_PULL:
  783. case FP_TELEPATHY:
  784. case FP_GRIP:
  785. case FP_LIGHTNING:
  786. case FP_DRAIN:
  787. return qfalse;
  788. break;
  789. default:
  790. break;
  791. }
  792. }
  793. }
  794. if ( (self->client->saber[0].saberFlags&SFL_TWO_HANDED)
  795. || (self->client->saber[0].model && self->client->saber[0].model[0]) )
  796. {//this saber requires the use of two hands OR our other hand is using an active saber too
  797. if ( (self->client->saber[0].forceRestrictions&(1<<forcePower)) )
  798. {//this power is verboten when using this saber
  799. return qfalse;
  800. }
  801. }
  802. if ( self->client->saber[0].model
  803. && self->client->saber[0].model[0] )
  804. {//both sabers on
  805. if ( g_saberRestrictForce.integer )
  806. {
  807. switch ( forcePower )
  808. {
  809. case FP_PUSH:
  810. case FP_PULL:
  811. case FP_TELEPATHY:
  812. case FP_GRIP:
  813. case FP_LIGHTNING:
  814. case FP_DRAIN:
  815. return qfalse;
  816. break;
  817. default:
  818. break;
  819. }
  820. }
  821. if ( (self->client->saber[1].forceRestrictions&(1<<forcePower)) )
  822. {//this power is verboten when using this saber
  823. return qfalse;
  824. }
  825. }
  826. }
  827. return WP_ForcePowerAvailable( self, forcePower, 0 ); // OVERRIDEFIXME
  828. }
  829. int WP_AbsorbConversion(gentity_t *attacked, int atdAbsLevel, gentity_t *attacker, int atPower, int atPowerLevel, int atForceSpent)
  830. {
  831. int getLevel = 0;
  832. int addTot = 0;
  833. gentity_t *abSound;
  834. if (atPower != FP_LIGHTNING &&
  835. atPower != FP_DRAIN &&
  836. atPower != FP_GRIP &&
  837. atPower != FP_PUSH &&
  838. atPower != FP_PULL)
  839. { //Only these powers can be absorbed
  840. return -1;
  841. }
  842. if (!atdAbsLevel)
  843. { //looks like attacker doesn't have any absorb power
  844. return -1;
  845. }
  846. if (!(attacked->client->ps.fd.forcePowersActive & (1 << FP_ABSORB)))
  847. { //absorb is not active
  848. return -1;
  849. }
  850. //Subtract absorb power level from the offensive force power
  851. getLevel = atPowerLevel;
  852. getLevel -= atdAbsLevel;
  853. if (getLevel < 0)
  854. {
  855. getLevel = 0;
  856. }
  857. //let the attacker absorb an amount of force used in this attack based on his level of absorb
  858. addTot = (atForceSpent/3)*attacked->client->ps.fd.forcePowerLevel[FP_ABSORB];
  859. if (addTot < 1 && atForceSpent >= 1)
  860. {
  861. addTot = 1;
  862. }
  863. attacked->client->ps.fd.forcePower += addTot;
  864. if (attacked->client->ps.fd.forcePower > 100)
  865. {
  866. attacked->client->ps.fd.forcePower = 100;
  867. }
  868. //play sound indicating that attack was absorbed
  869. if (attacked->client->forcePowerSoundDebounce < level.time)
  870. {
  871. abSound = G_PreDefSound(attacked->client->ps.origin, PDSOUND_ABSORBHIT);
  872. abSound->s.trickedentindex = attacked->s.number;
  873. attacked->client->forcePowerSoundDebounce = level.time + 400;
  874. }
  875. return getLevel;
  876. }
  877. void WP_ForcePowerRegenerate( gentity_t *self, int overrideAmt )
  878. { //called on a regular interval to regenerate force power.
  879. if ( !self->client )
  880. {
  881. return;
  882. }
  883. if ( overrideAmt )
  884. { //custom regen amount
  885. self->client->ps.fd.forcePower += overrideAmt;
  886. }
  887. else
  888. { //otherwise, just 1
  889. self->client->ps.fd.forcePower++;
  890. }
  891. if ( self->client->ps.fd.forcePower > self->client->ps.fd.forcePowerMax )
  892. { //cap it off at the max (default 100)
  893. self->client->ps.fd.forcePower = self->client->ps.fd.forcePowerMax;
  894. }
  895. }
  896. void WP_ForcePowerStart( gentity_t *self, forcePowers_t forcePower, int overrideAmt )
  897. { //activate the given force power
  898. int duration = 0;
  899. qboolean hearable = qfalse;
  900. float hearDist = 0;
  901. if (!WP_ForcePowerAvailable( self, forcePower, overrideAmt ))
  902. {
  903. return;
  904. }
  905. if ( BG_FullBodyTauntAnim( self->client->ps.legsAnim ) )
  906. {//stop taunt
  907. self->client->ps.legsTimer = 0;
  908. }
  909. if ( BG_FullBodyTauntAnim( self->client->ps.torsoAnim ) )
  910. {//stop taunt
  911. self->client->ps.torsoTimer = 0;
  912. }
  913. //hearable and hearDist are merely for the benefit of bots, and not related to if a sound is actually played.
  914. //If duration is set, the force power will assume to be timer-based.
  915. switch( (int)forcePower )
  916. {
  917. case FP_HEAL:
  918. hearable = qtrue;
  919. hearDist = 256;
  920. self->client->ps.fd.forcePowersActive |= ( 1 << forcePower );
  921. break;
  922. case FP_LEVITATION:
  923. hearable = qtrue;
  924. hearDist = 256;
  925. self->client->ps.fd.forcePowersActive |= ( 1 << forcePower );
  926. break;
  927. case FP_SPEED:
  928. hearable = qtrue;
  929. hearDist = 256;
  930. if (self->client->ps.fd.forcePowerLevel[FP_SPEED] == FORCE_LEVEL_1)
  931. {
  932. duration = 10000;
  933. }
  934. else if (self->client->ps.fd.forcePowerLevel[FP_SPEED] == FORCE_LEVEL_2)
  935. {
  936. duration = 15000;
  937. }
  938. else if (self->client->ps.fd.forcePowerLevel[FP_SPEED] == FORCE_LEVEL_3)
  939. {
  940. duration = 20000;
  941. }
  942. else //shouldn't get here
  943. {
  944. break;
  945. }
  946. if (overrideAmt)
  947. {
  948. duration = overrideAmt;
  949. }
  950. self->client->ps.fd.forcePowersActive |= ( 1 << forcePower );
  951. break;
  952. case FP_PUSH:
  953. hearable = qtrue;
  954. hearDist = 256;
  955. break;
  956. case FP_PULL:
  957. hearable = qtrue;
  958. hearDist = 256;
  959. break;
  960. case FP_TELEPATHY:
  961. hearable = qtrue;
  962. hearDist = 256;
  963. if (self->client->ps.fd.forcePowerLevel[FP_TELEPATHY] == FORCE_LEVEL_1)
  964. {
  965. duration = 20000;
  966. }
  967. else if (self->client->ps.fd.forcePowerLevel[FP_TELEPATHY] == FORCE_LEVEL_2)
  968. {
  969. duration = 25000;
  970. }
  971. else if (self->client->ps.fd.forcePowerLevel[FP_TELEPATHY] == FORCE_LEVEL_3)
  972. {
  973. duration = 30000;
  974. }
  975. else //shouldn't get here
  976. {
  977. break;
  978. }
  979. self->client->ps.fd.forcePowersActive |= ( 1 << forcePower );
  980. break;
  981. case FP_GRIP:
  982. hearable = qtrue;
  983. hearDist = 256;
  984. self->client->ps.fd.forcePowersActive |= ( 1 << forcePower );
  985. self->client->ps.powerups[PW_DISINT_4] = level.time + 60000;
  986. break;
  987. case FP_LIGHTNING:
  988. hearable = qtrue;
  989. hearDist = 512;
  990. duration = overrideAmt;
  991. overrideAmt = 0;
  992. self->client->ps.fd.forcePowersActive |= ( 1 << forcePower );
  993. self->client->ps.activeForcePass = self->client->ps.fd.forcePowerLevel[FP_LIGHTNING];
  994. break;
  995. case FP_RAGE:
  996. hearable = qtrue;
  997. hearDist = 256;
  998. if (self->client->ps.fd.forcePowerLevel[FP_RAGE] == FORCE_LEVEL_1)
  999. {
  1000. duration = 8000;
  1001. }
  1002. else if (self->client->ps.fd.forcePowerLevel[FP_RAGE] == FORCE_LEVEL_2)
  1003. {
  1004. duration = 14000;
  1005. }
  1006. else if (self->client->ps.fd.forcePowerLevel[FP_RAGE] == FORCE_LEVEL_3)
  1007. {
  1008. duration = 20000;
  1009. }
  1010. else //shouldn't get here
  1011. {
  1012. break;
  1013. }
  1014. self->client->ps.fd.forcePowersActive |= ( 1 << forcePower );
  1015. break;
  1016. case FP_PROTECT:
  1017. hearable = qtrue;
  1018. hearDist = 256;
  1019. duration = 20000;
  1020. self->client->ps.fd.forcePowersActive |= ( 1 << forcePower );
  1021. break;
  1022. case FP_ABSORB:
  1023. hearable = qtrue;
  1024. hearDist = 256;
  1025. duration = 20000;
  1026. self->client->ps.fd.forcePowersActive |= ( 1 << forcePower );
  1027. break;
  1028. case FP_TEAM_HEAL:
  1029. hearable = qtrue;
  1030. hearDist = 256;
  1031. self->client->ps.fd.forcePowersActive |= ( 1 << forcePower );
  1032. break;
  1033. case FP_TEAM_FORCE:
  1034. hearable = qtrue;
  1035. hearDist = 256;
  1036. self->client->ps.fd.forcePowersActive |= ( 1 << forcePower );
  1037. break;
  1038. case FP_DRAIN:
  1039. hearable = qtrue;
  1040. hearDist = 256;
  1041. duration = overrideAmt;
  1042. overrideAmt = 0;
  1043. self->client->ps.fd.forcePowersActive |= ( 1 << forcePower );
  1044. //self->client->ps.activeForcePass = self->client->ps.fd.forcePowerLevel[FP_DRAIN];
  1045. break;
  1046. case FP_SEE:
  1047. hearable = qtrue;
  1048. hearDist = 256;
  1049. if (self->client->ps.fd.forcePowerLevel[FP_SEE] == FORCE_LEVEL_1)
  1050. {
  1051. duration = 10000;
  1052. }
  1053. else if (self->client->ps.fd.forcePowerLevel[FP_SEE] == FORCE_LEVEL_2)
  1054. {
  1055. duration = 20000;
  1056. }
  1057. else if (self->client->ps.fd.forcePowerLevel[FP_SEE] == FORCE_LEVEL_3)
  1058. {
  1059. duration = 30000;
  1060. }
  1061. else //shouldn't get here
  1062. {
  1063. break;
  1064. }
  1065. self->client->ps.fd.forcePowersActive |= ( 1 << forcePower );
  1066. break;
  1067. case FP_SABER_OFFENSE:
  1068. break;
  1069. case FP_SABER_DEFENSE:
  1070. break;
  1071. case FP_SABERTHROW:
  1072. break;
  1073. default:
  1074. break;
  1075. }
  1076. if ( duration )
  1077. {
  1078. self->client->ps.fd.forcePowerDuration[forcePower] = level.time + duration;
  1079. }
  1080. else
  1081. {
  1082. self->client->ps.fd.forcePowerDuration[forcePower] = 0;
  1083. }
  1084. if (hearable)
  1085. {
  1086. self->client->ps.otherSoundLen = hearDist;
  1087. self->client->ps.otherSoundTime = level.time + 100;
  1088. }
  1089. self->client->ps.fd.forcePowerDebounce[forcePower] = 0;
  1090. if ((int)forcePower == FP_SPEED && overrideAmt)
  1091. {
  1092. BG_ForcePowerDrain( &self->client->ps, forcePower, overrideAmt*0.025 );
  1093. }
  1094. else if ((int)forcePower != FP_GRIP && (int)forcePower != FP_DRAIN)
  1095. { //grip and drain drain as damage is done
  1096. BG_ForcePowerDrain( &self->client->ps, forcePower, overrideAmt );
  1097. }
  1098. }
  1099. void ForceHeal( gentity_t *self )
  1100. {
  1101. if ( self->health <= 0 )
  1102. {
  1103. return;
  1104. }
  1105. if ( !WP_ForcePowerUsable( self, FP_HEAL ) )
  1106. {
  1107. return;
  1108. }
  1109. if ( self->health >= self->client->ps.stats[STAT_MAX_HEALTH])
  1110. {
  1111. return;
  1112. }
  1113. if (self->client->ps.fd.forcePowerLevel[FP_HEAL] == FORCE_LEVEL_3)
  1114. {
  1115. self->health += 25; //This was 50, but that angered the Balance God.
  1116. if (self->health > self->client->ps.stats[STAT_MAX_HEALTH])
  1117. {
  1118. self->health = self->client->ps.stats[STAT_MAX_HEALTH];
  1119. }
  1120. BG_ForcePowerDrain( &self->client->ps, FP_HEAL, 0 );
  1121. }
  1122. else if (self->client->ps.fd.forcePowerLevel[FP_HEAL] == FORCE_LEVEL_2)
  1123. {
  1124. self->health += 10;
  1125. if (self->health > self->client->ps.stats[STAT_MAX_HEALTH])
  1126. {
  1127. self->health = self->client->ps.stats[STAT_MAX_HEALTH];
  1128. }
  1129. BG_ForcePowerDrain( &self->client->ps, FP_HEAL, 0 );
  1130. }
  1131. else
  1132. {
  1133. self->health += 5;
  1134. if (self->health > self->client->ps.stats[STAT_MAX_HEALTH])
  1135. {
  1136. self->health = self->client->ps.stats[STAT_MAX_HEALTH];
  1137. }
  1138. BG_ForcePowerDrain( &self->client->ps, FP_HEAL, 0 );
  1139. }
  1140. /*
  1141. else
  1142. {
  1143. WP_ForcePowerStart( self, FP_HEAL, 0 );
  1144. }
  1145. */
  1146. //NOTE: Decided to make all levels instant.
  1147. G_Sound( self, CHAN_ITEM, G_SoundIndex("sound/weapons/force/heal.wav") );
  1148. }
  1149. void WP_AddToClientBitflags(gentity_t *ent, int entNum)
  1150. {
  1151. if (!ent)
  1152. {
  1153. return;
  1154. }
  1155. if (entNum > 47)
  1156. {
  1157. ent->s.trickedentindex4 |= (1 << (entNum-48));
  1158. }
  1159. else if (entNum > 31)
  1160. {
  1161. ent->s.trickedentindex3 |= (1 << (entNum-32));
  1162. }
  1163. else if (entNum > 15)
  1164. {
  1165. ent->s.trickedentindex2 |= (1 << (entNum-16));
  1166. }
  1167. else
  1168. {
  1169. ent->s.trickedentindex |= (1 << entNum);
  1170. }
  1171. }
  1172. void ForceTeamHeal( gentity_t *self )
  1173. {
  1174. float radius = 256;
  1175. int i = 0;
  1176. gentity_t *ent;
  1177. vec3_t a;
  1178. int numpl = 0;
  1179. int pl[MAX_CLIENTS];
  1180. int healthadd = 0;
  1181. gentity_t *te = NULL;
  1182. if ( self->health <= 0 )
  1183. {
  1184. return;
  1185. }
  1186. if ( !WP_ForcePowerUsable( self, FP_TEAM_HEAL ) )
  1187. {
  1188. return;
  1189. }
  1190. if (self->client->ps.fd.forcePowerDebounce[FP_TEAM_HEAL] >= level.time)
  1191. {
  1192. return;
  1193. }
  1194. if (self->client->ps.fd.forcePowerLevel[FP_TEAM_HEAL] == FORCE_LEVEL_2)
  1195. {
  1196. radius *= 1.5;
  1197. }
  1198. if (self->client->ps.fd.forcePowerLevel[FP_TEAM_HEAL] == FORCE_LEVEL_3)
  1199. {
  1200. radius *= 2;
  1201. }
  1202. while (i < MAX_CLIENTS)
  1203. {
  1204. ent = &g_entities[i];
  1205. if (ent && ent->client && self != ent && OnSameTeam(self, ent) && ent->client->ps.stats[STAT_HEALTH] < ent->client->ps.stats[STAT_MAX_HEALTH] && ent->client->ps.stats[STAT_HEALTH] > 0 && ForcePowerUsableOn(self, ent, FP_TEAM_HEAL) &&
  1206. trap_InPVS(self->client->ps.origin, ent->client->ps.origin))
  1207. {
  1208. VectorSubtract(self->client->ps.origin, ent->client->ps.origin, a);
  1209. if (VectorLength(a) <= radius)
  1210. {
  1211. pl[numpl] = i;
  1212. numpl++;
  1213. }
  1214. }
  1215. i++;
  1216. }
  1217. if (numpl < 1)
  1218. {
  1219. return;
  1220. }
  1221. if (numpl == 1)
  1222. {
  1223. healthadd = 50;
  1224. }
  1225. else if (numpl == 2)
  1226. {
  1227. healthadd = 33;
  1228. }
  1229. else
  1230. {
  1231. healthadd = 25;
  1232. }
  1233. self->client->ps.fd.forcePowerDebounce[FP_TEAM_HEAL] = level.time + 2000;
  1234. i = 0;
  1235. while (i < numpl)
  1236. {
  1237. if (g_entities[pl[i]].client->ps.stats[STAT_HEALTH] > 0 &&
  1238. g_entities[pl[i]].health > 0)
  1239. {
  1240. g_entities[pl[i]].client->ps.stats[STAT_HEALTH] += healthadd;
  1241. if (g_entities[pl[i]].client->ps.stats[STAT_HEALTH] > g_entities[pl[i]].client->ps.stats[STAT_MAX_HEALTH])
  1242. {
  1243. g_entities[pl[i]].client->ps.stats[STAT_HEALTH] = g_entities[pl[i]].client->ps.stats[STAT_MAX_HEALTH];
  1244. }
  1245. g_entities[pl[i]].health = g_entities[pl[i]].client->ps.stats[STAT_HEALTH];
  1246. //At this point we know we got one, so add him into the collective event client bitflag
  1247. if (!te)
  1248. {
  1249. te = G_TempEntity( self->client->ps.origin, EV_TEAM_POWER);
  1250. te->s.eventParm = 1; //eventParm 1 is heal, eventParm 2 is force regen
  1251. //since we had an extra check above, do the drain now because we got at least one guy
  1252. BG_ForcePowerDrain( &self->client->ps, FP_TEAM_HEAL, forcePowerNeeded[self->client->ps.fd.forcePowerLevel[FP_TEAM_HEAL]][FP_TEAM_HEAL] );
  1253. }
  1254. WP_AddToClientBitflags(te, pl[i]);
  1255. //Now cramming it all into one event.. doing this many g_sound events at once was a Bad Thing.
  1256. }
  1257. i++;
  1258. }
  1259. }
  1260. void ForceTeamForceReplenish( gentity_t *self )
  1261. {
  1262. float radius = 256;
  1263. int i = 0;
  1264. gentity_t *ent;
  1265. vec3_t a;
  1266. int numpl = 0;
  1267. int pl[MAX_CLIENTS];
  1268. int poweradd = 0;
  1269. gentity_t *te = NULL;
  1270. if ( self->health <= 0 )
  1271. {
  1272. return;
  1273. }
  1274. if ( !WP_ForcePowerUsable( self, FP_TEAM_FORCE ) )
  1275. {
  1276. return;
  1277. }
  1278. if (self->client->ps.fd.forcePowerDebounce[FP_TEAM_FORCE] >= level.time)
  1279. {
  1280. return;
  1281. }
  1282. if (self->client->ps.fd.forcePowerLevel[FP_TEAM_FORCE] == FORCE_LEVEL_2)
  1283. {
  1284. radius *= 1.5;
  1285. }
  1286. if (self->client->ps.fd.forcePowerLevel[FP_TEAM_FORCE] == FORCE_LEVEL_3)
  1287. {
  1288. radius *= 2;
  1289. }
  1290. while (i < MAX_CLIENTS)
  1291. {
  1292. ent = &g_entities[i];
  1293. if (ent && ent->client && self != ent && OnSameTeam(self, ent) && ent->client->ps.fd.forcePower < 100 && ForcePowerUsableOn(self, ent, FP_TEAM_FORCE) &&
  1294. trap_InPVS(self->client->ps.origin, ent->client->ps.origin))
  1295. {
  1296. VectorSubtract(self->client->ps.origin, ent->client->ps.origin, a);
  1297. if (VectorLength(a) <= radius)
  1298. {
  1299. pl[numpl] = i;
  1300. numpl++;
  1301. }
  1302. }
  1303. i++;
  1304. }
  1305. if (numpl < 1)
  1306. {
  1307. return;
  1308. }
  1309. if (numpl == 1)
  1310. {
  1311. poweradd = 50;
  1312. }
  1313. else if (numpl == 2)
  1314. {
  1315. poweradd = 33;
  1316. }
  1317. else
  1318. {
  1319. poweradd = 25;
  1320. }
  1321. self->client->ps.fd.forcePowerDebounce[FP_TEAM_FORCE] = level.time + 2000;
  1322. BG_ForcePowerDrain( &self->client->ps, FP_TEAM_FORCE, forcePowerNeeded[self->client->ps.fd.forcePowerLevel[FP_TEAM_FORCE]][FP_TEAM_FORCE] );
  1323. i = 0;
  1324. while (i < numpl)
  1325. {
  1326. g_entities[pl[i]].client->ps.fd.forcePower += poweradd;
  1327. if (g_entities[pl[i]].client->ps.fd.forcePower > 100)
  1328. {
  1329. g_entities[pl[i]].client->ps.fd.forcePower = 100;
  1330. }
  1331. //At this point we know we got one, so add him into the collective event client bitflag
  1332. if (!te)
  1333. {
  1334. te = G_TempEntity( self->client->ps.origin, EV_TEAM_POWER);
  1335. te->s.eventParm = 2; //eventParm 1 is heal, eventParm 2 is force regen
  1336. }
  1337. WP_AddToClientBitflags(te, pl[i]);
  1338. //Now cramming it all into one event.. doing this many g_sound events at once was a Bad Thing.
  1339. i++;
  1340. }
  1341. }
  1342. void ForceGrip( gentity_t *self )
  1343. {
  1344. trace_t tr;
  1345. vec3_t tfrom, tto, fwd;
  1346. if ( self->health <= 0 )
  1347. {
  1348. return;
  1349. }
  1350. if (self->client->ps.forceHandExtend != HANDEXTEND_NONE)
  1351. {
  1352. return;
  1353. }
  1354. if (self->client->ps.weaponTime > 0)
  1355. {
  1356. return;
  1357. }
  1358. if (self->client->ps.fd.forceGripUseTime > level.time)
  1359. {
  1360. return;
  1361. }
  1362. if ( !WP_ForcePowerUsable( self, FP_GRIP ) )
  1363. {
  1364. return;
  1365. }
  1366. VectorCopy(self->client->ps.origin, tfrom);
  1367. tfrom[2] += self->client->ps.viewheight;
  1368. AngleVectors(self->client->ps.viewangles, fwd, NULL, NULL);
  1369. tto[0] = tfrom[0] + fwd[0]*MAX_GRIP_DISTANCE;
  1370. tto[1] = tfrom[1] + fwd[1]*MAX_GRIP_DISTANCE;
  1371. tto[2] = tfrom[2] + fwd[2]*MAX_GRIP_DISTANCE;
  1372. trap_Trace(&tr, tfrom, NULL, NULL, tto, self->s.number, MASK_PLAYERSOLID);
  1373. if ( tr.fraction != 1.0 &&
  1374. tr.entityNum != ENTITYNUM_NONE &&
  1375. g_entities[tr.entityNum].client &&
  1376. !g_entities[tr.entityNum].client->ps.fd.forceGripCripple &&
  1377. g_entities[tr.entityNum].client->ps.fd.forceGripBeingGripped < level.time &&
  1378. ForcePowerUsableOn(self, &g_entities[tr.entityNum], FP_GRIP) &&
  1379. (g_friendlyFire.integer || !OnSameTeam(self, &g_entities[tr.entityNum])) ) //don't grip someone who's still crippled
  1380. {
  1381. if (g_entities[tr.entityNum].s.number < MAX_CLIENTS && g_entities[tr.entityNum].client->ps.m_iVehicleNum)
  1382. { //a player on a vehicle
  1383. gentity_t *vehEnt = &g_entities[g_entities[tr.entityNum].client->ps.m_iVehicleNum];
  1384. if (vehEnt->inuse && vehEnt->client && vehEnt->m_pVehicle)
  1385. {
  1386. if (vehEnt->m_pVehicle->m_pVehicleInfo->type == VH_SPEEDER ||
  1387. vehEnt->m_pVehicle->m_pVehicleInfo->type == VH_ANIMAL)
  1388. { //push the guy off
  1389. vehEnt->m_pVehicle->m_pVehicleInfo->Eject(vehEnt->m_pVehicle, (bgEntity_t *)&g_entities[tr.entityNum], qfalse);
  1390. }
  1391. }
  1392. }
  1393. self->client->ps.fd.forceGripEntityNum = tr.entityNum;
  1394. g_entities[tr.entityNum].client->ps.fd.forceGripStarted = level.time;
  1395. self->client->ps.fd.forceGripDamageDebounceTime = 0;
  1396. self->client->ps.forceHandExtend = HANDEXTEND_FORCE_HOLD;
  1397. self->client->ps.forceHandExtendTime = level.time + 5000;
  1398. }
  1399. else
  1400. {
  1401. self->client->ps.fd.forceGripEntityNum = ENTITYNUM_NONE;
  1402. return;
  1403. }
  1404. }
  1405. void ForceSpeed( gentity_t *self, int forceDuration )
  1406. {
  1407. if ( self->health <= 0 )
  1408. {
  1409. return;
  1410. }
  1411. if (self->client->ps.forceAllowDeactivateTime < level.time &&
  1412. (self->client->ps.fd.forcePowersActive & (1 << FP_SPEED)) )
  1413. {
  1414. WP_ForcePowerStop( self, FP_SPEED );
  1415. return;
  1416. }
  1417. if ( !WP_ForcePowerUsable( self, FP_SPEED ) )
  1418. {
  1419. return;
  1420. }
  1421. if ( self->client->holdingObjectiveItem >= MAX_CLIENTS
  1422. && self->client->holdingObjectiveItem < ENTITYNUM_WORLD )
  1423. {//holding Siege item
  1424. if ( g_entities[self->client->holdingObjectiveItem].genericValue15 )
  1425. {//disables force powers
  1426. return;
  1427. }
  1428. }
  1429. self->client->ps.forceAllowDeactivateTime = level.time + 1500;
  1430. WP_ForcePowerStart( self, FP_SPEED, forceDuration );
  1431. G_Sound( self, CHAN_BODY, G_SoundIndex("sound/weapons/force/speed.wav") );
  1432. G_Sound( self, TRACK_CHANNEL_2, speedLoopSound );
  1433. }
  1434. void ForceSeeing( gentity_t *self )
  1435. {
  1436. if ( self->health <= 0 )
  1437. {
  1438. return;
  1439. }
  1440. if (self->client->ps.forceAllowDeactivateTime < level.time &&
  1441. (self->client->ps.fd.forcePowersActive & (1 << FP_SEE)) )
  1442. {
  1443. WP_ForcePowerStop( self, FP_SEE );
  1444. return;
  1445. }
  1446. if ( !WP_ForcePowerUsable( self, FP_SEE ) )
  1447. {
  1448. return;
  1449. }
  1450. self->client->ps.forceAllowDeactivateTime = level.time + 1500;
  1451. WP_ForcePowerStart( self, FP_SEE, 0 );
  1452. G_Sound( self, CHAN_AUTO, G_SoundIndex("sound/weapons/force/see.wav") );
  1453. G_Sound( self, TRACK_CHANNEL_5, seeLoopSound );
  1454. }
  1455. void ForceProtect( gentity_t *self )
  1456. {
  1457. if ( self->health <= 0 )
  1458. {
  1459. return;
  1460. }
  1461. if (self->client->ps.forceAllowDeactivateTime < level.time &&
  1462. (self->client->ps.fd.forcePowersActive & (1 << FP_PROTECT)) )
  1463. {
  1464. WP_ForcePowerStop( self, FP_PROTECT );
  1465. return;
  1466. }
  1467. if ( !WP_ForcePowerUsable( self, FP_PROTECT ) )
  1468. {
  1469. return;
  1470. }
  1471. // Make sure to turn off Force Rage and Force Absorb.
  1472. if (self->client->ps.fd.forcePowersActive & (1 << FP_RAGE) )
  1473. {
  1474. WP_ForcePowerStop( self, FP_RAGE );
  1475. }
  1476. if (self->client->ps.fd.forcePowersActive & (1 << FP_ABSORB) )
  1477. {
  1478. WP_ForcePowerStop( self, FP_ABSORB );
  1479. }
  1480. self->client->ps.forceAllowDeactivateTime = level.time + 1500;
  1481. WP_ForcePowerStart( self, FP_PROTECT, 0 );
  1482. G_PreDefSound(self->client->ps.origin, PDSOUND_PROTECT);
  1483. G_Sound( self, TRACK_CHANNEL_3, protectLoopSound );
  1484. }
  1485. void ForceAbsorb( gentity_t *self )
  1486. {
  1487. if ( self->health <= 0 )
  1488. {
  1489. return;
  1490. }
  1491. if (self->client->ps.forceAllowDeactivateTime < level.time &&
  1492. (self->client->ps.fd.forcePowersActive & (1 << FP_ABSORB)) )
  1493. {
  1494. WP_ForcePowerStop( self, FP_ABSORB );
  1495. return;
  1496. }
  1497. if ( !WP_ForcePowerUsable( self, FP_ABSORB ) )
  1498. {
  1499. return;
  1500. }
  1501. // Make sure to turn off Force Rage and Force Protection.
  1502. if (self->client->ps.fd.forcePowersActive & (1 << FP_RAGE) )
  1503. {
  1504. WP_ForcePowerStop( self, FP_RAGE );
  1505. }
  1506. if (self->client->ps.fd.forcePowersActive & (1 << FP_PROTECT) )
  1507. {
  1508. WP_ForcePowerStop( self, FP_PROTECT );
  1509. }
  1510. self->client->ps.forceAllowDeactivateTime = level.time + 1500;
  1511. WP_ForcePowerStart( self, FP_ABSORB, 0 );
  1512. G_PreDefSound(self->client->ps.origin, PDSOUND_ABSORB);
  1513. G_Sound( self, TRACK_CHANNEL_3, absorbLoopSound );
  1514. }
  1515. void ForceRage( gentity_t *self )
  1516. {
  1517. if ( self->health <= 0 )
  1518. {
  1519. return;
  1520. }
  1521. if (self->client->ps.forceAllowDeactivateTime < level.time &&
  1522. (self->client->ps.fd.forcePowersActive & (1 << FP_RAGE)) )
  1523. {
  1524. WP_ForcePowerStop( self, FP_RAGE );
  1525. return;
  1526. }
  1527. if ( !WP_ForcePowerUsable( self, FP_RAGE ) )
  1528. {
  1529. return;
  1530. }
  1531. if (self->client->ps.fd.forceRageRecoveryTime >= level.time)
  1532. {
  1533. return;
  1534. }
  1535. if (self->health < 10)
  1536. {
  1537. return;
  1538. }
  1539. // Make sure to turn off Force Protection and Force Absorb.
  1540. if (self->client->ps.fd.forcePowersActive & (1 << FP_PROTECT) )
  1541. {
  1542. WP_ForcePowerStop( self, FP_PROTECT );
  1543. }
  1544. if (self->client->ps.fd.forcePowersActive & (1 << FP_ABSORB) )
  1545. {
  1546. WP_ForcePowerStop( self, FP_ABSORB );
  1547. }
  1548. self->client->ps.forceAllowDeactivateTime = level.time + 1500;
  1549. WP_ForcePowerStart( self, FP_RAGE, 0 );
  1550. G_Sound( self, TRACK_CHANNEL_4, G_SoundIndex("sound/weapons/force/rage.wav") );
  1551. G_Sound( self, TRACK_CHANNEL_3, rageLoopSound );
  1552. }
  1553. void ForceLightning( gentity_t *self )
  1554. {
  1555. if ( self->health <= 0 )
  1556. {
  1557. return;
  1558. }
  1559. if ( self->client->ps.fd.forcePower < 25 || !WP_ForcePowerUsable( self, FP_LIGHTNING ) )
  1560. {
  1561. return;
  1562. }
  1563. if ( self->client->ps.fd.forcePowerDebounce[FP_LIGHTNING] > level.time )
  1564. {//stops it while using it and also after using it, up to 3 second delay
  1565. return;
  1566. }
  1567. if (self->client->ps.forceHandExtend != HANDEXTEND_NONE)
  1568. {
  1569. return;
  1570. }
  1571. if (self->client->ps.weaponTime > 0)
  1572. {
  1573. return;
  1574. }
  1575. // fix: rocket lock bug
  1576. BG_ClearRocketLock(&self->client->ps);
  1577. //Shoot lightning from hand
  1578. //using grip anim now, to extend the burst time
  1579. self->client->ps.forceHandExtend = HANDEXTEND_FORCE_HOLD;
  1580. self->client->ps.forceHandExtendTime = level.time + 20000;
  1581. G_Sound( self, CHAN_BODY, G_SoundIndex("sound/weapons/force/lightning") );
  1582. WP_ForcePowerStart( self, FP_LIGHTNING, 500 );
  1583. }
  1584. void ForceLightningDamage( gentity_t *self, gentity_t *traceEnt, vec3_t dir, vec3_t impactPoint )
  1585. {
  1586. self->client->dangerTime = level.time;
  1587. self->client->ps.eFlags &= ~EF_INVULNERABLE;
  1588. self->client->invulnerableTimer = 0;
  1589. if ( traceEnt && traceEnt->takedamage )
  1590. {
  1591. if (!traceEnt->client && traceEnt->s.eType == ET_NPC)
  1592. { //g2animent
  1593. if (traceEnt->s.genericenemyindex < level.time)
  1594. {
  1595. traceEnt->s.genericenemyindex = level.time + 2000;
  1596. }
  1597. }
  1598. if ( traceEnt->client )
  1599. {//an enemy or object
  1600. if (traceEnt->client->noLightningTime >= level.time)
  1601. { //give them power and don't hurt them.
  1602. traceEnt->client->ps.fd.forcePower++;
  1603. if (traceEnt->client->ps.fd.forcePower > 100)
  1604. {
  1605. traceEnt->client->ps.fd.forcePower = 100;
  1606. }
  1607. return;
  1608. }
  1609. if (ForcePowerUsableOn(self, traceEnt, FP_LIGHTNING))
  1610. {
  1611. int dmg = Q_irand(1,2); //Q_irand( 1, 3 );
  1612. int modPowerLevel = -1;
  1613. if (traceEnt->client)
  1614. {
  1615. modPowerLevel = WP_AbsorbConversion(traceEnt, traceEnt->client->ps.fd.forcePowerLevel[FP_ABSORB], self, FP_LIGHTNING, self->client->ps.fd.forcePowerLevel[FP_LIGHTNING], 1);
  1616. }
  1617. if (modPowerLevel != -1)
  1618. {
  1619. if (!modPowerLevel)
  1620. {
  1621. dmg = 0;
  1622. traceEnt->client->noLightningTime = level.time + 400;
  1623. }
  1624. else if (modPowerLevel == 1)
  1625. {
  1626. dmg = 1;
  1627. traceEnt->client->noLightningTime = level.time + 300;
  1628. }
  1629. else if (modPowerLevel == 2)
  1630. {
  1631. dmg = 1;
  1632. traceEnt->client->noLightningTime = level.time + 100;
  1633. }
  1634. }
  1635. if ( self->client->ps.weapon == WP_MELEE
  1636. && self->client->ps.fd.forcePowerLevel[FP_LIGHTNING] > FORCE_LEVEL_2 )
  1637. {//2-handed lightning
  1638. //jackin' 'em up, Palpatine-style
  1639. dmg *= 2;
  1640. }
  1641. if (dmg)
  1642. {
  1643. //rww - Shields can now absorb lightning too.
  1644. G_Damage( traceEnt, self, self, dir, impactPoint, dmg, 0, MOD_FORCE_DARK );
  1645. }
  1646. if ( traceEnt->client )
  1647. {
  1648. if ( !Q_irand( 0, 2 ) )
  1649. {
  1650. G_Sound( traceEnt, CHAN_BODY, G_SoundIndex( va("sound/weapons/force/lightninghit%i", Q_irand(1, 3) )) );
  1651. }
  1652. if (traceEnt->client->ps.electrifyTime < (level.time + 400))
  1653. { //only update every 400ms to reduce bandwidth usage (as it is passing a 32-bit time value)
  1654. traceEnt->client->ps.electrifyTime = level.time + 800;
  1655. }
  1656. if ( traceEnt->client->ps.powerups[PW_CLOAKED] )
  1657. {//disable cloak temporarily
  1658. Jedi_Decloak( traceEnt );
  1659. traceEnt->client->cloakToggleTime = level.time + Q_irand( 3000, 10000 );
  1660. }
  1661. }
  1662. }
  1663. }
  1664. }
  1665. }
  1666. void ForceShootLightning( gentity_t *self )
  1667. {
  1668. trace_t tr;
  1669. vec3_t end, forward;
  1670. gentity_t *traceEnt;
  1671. if ( self->health <= 0 )
  1672. {
  1673. return;
  1674. }
  1675. AngleVectors( self->client->ps.viewangles, forward, NULL, NULL );
  1676. VectorNormalize( forward );
  1677. if ( self->client->ps.fd.forcePowerLevel[FP_LIGHTNING] > FORCE_LEVEL_2 )
  1678. {//arc
  1679. vec3_t center, mins, maxs, dir, ent_org, size, v;
  1680. float radius = FORCE_LIGHTNING_RADIUS, dot, dist;
  1681. gentity_t *entityList[MAX_GENTITIES];
  1682. int iEntityList[MAX_GENTITIES];
  1683. int e, numListedEntities, i;
  1684. VectorCopy( self->client->ps.origin, center );
  1685. for ( i = 0 ; i < 3 ; i++ )
  1686. {
  1687. mins[i] = center[i] - radius;
  1688. maxs[i] = center[i] + radius;
  1689. }
  1690. numListedEntities = trap_EntitiesInBox( mins, maxs, iEntityList, MAX_GENTITIES );
  1691. i = 0;
  1692. while (i < numListedEntities)
  1693. {
  1694. entityList[i] = &g_entities[iEntityList[i]];
  1695. i++;
  1696. }
  1697. for ( e = 0 ; e < numListedEntities ; e++ )
  1698. {
  1699. traceEnt = entityList[e];
  1700. if ( !traceEnt )
  1701. continue;
  1702. if ( traceEnt == self )
  1703. continue;
  1704. if ( traceEnt->r.ownerNum == self->s.number && traceEnt->s.weapon != WP_THERMAL )//can push your own thermals
  1705. continue;
  1706. if ( !traceEnt->inuse )
  1707. continue;
  1708. if ( !traceEnt->takedamage )
  1709. continue;
  1710. if ( traceEnt->health <= 0 )//no torturing corpses
  1711. continue;
  1712. if ( !g_friendlyFire.integer && OnSameTeam(self, traceEnt))
  1713. continue;
  1714. //this is all to see if we need to start a saber attack, if it's in flight, this doesn't matter
  1715. // find the distance from the edge of the bounding box
  1716. for ( i = 0 ; i < 3 ; i++ )
  1717. {
  1718. if ( center[i] < traceEnt->r.absmin[i] )
  1719. {
  1720. v[i] = traceEnt->r.absmin[i] - center[i];
  1721. } else if ( center[i] > traceEnt->r.absmax[i] )
  1722. {
  1723. v[i] = center[i] - traceEnt->r.absmax[i];
  1724. } else
  1725. {
  1726. v[i] = 0;
  1727. }
  1728. }
  1729. VectorSubtract( traceEnt->r.absmax, traceEnt->r.absmin, size );
  1730. VectorMA( traceEnt->r.absmin, 0.5, size, ent_org );
  1731. //see if they're in front of me
  1732. //must be within the forward cone
  1733. VectorSubtract( ent_org, center, dir );
  1734. VectorNormalize( dir );
  1735. if ( (dot = DotProduct( dir, forward )) < 0.5 )
  1736. continue;
  1737. //must be close enough
  1738. dist = VectorLength( v );
  1739. if ( dist >= radius )
  1740. {
  1741. continue;
  1742. }
  1743. //in PVS?
  1744. if ( !traceEnt->r.bmodel && !trap_InPVS( ent_org, self->client->ps.origin ) )
  1745. {//must be in PVS
  1746. continue;
  1747. }
  1748. //Now check and see if we can actually hit it
  1749. trap_Trace( &tr, self->client->ps.origin, vec3_origin, vec3_origin, ent_org, self->s.number, MASK_SHOT );
  1750. if ( tr.fraction < 1.0f && tr.entityNum != traceEnt->s.number )
  1751. {//must have clear LOS
  1752. continue;
  1753. }
  1754. // ok, we are within the radius, add us to the incoming list
  1755. ForceLightningDamage( self, traceEnt, dir, ent_org );
  1756. }
  1757. }
  1758. else
  1759. {//trace-line
  1760. VectorMA( self->client->ps.origin, 2048, forward, end );
  1761. trap_Trace( &tr, self->client->ps.origin, vec3_origin, vec3_origin, end, self->s.number, MASK_SHOT );
  1762. if ( tr.entityNum == ENTITYNUM_NONE || tr.fraction == 1.0 || tr.allsolid || tr.startsolid )
  1763. {
  1764. return;
  1765. }
  1766. traceEnt = &g_entities[tr.entityNum];
  1767. ForceLightningDamage( self, traceEnt, forward, tr.endpos );
  1768. }
  1769. }
  1770. void ForceDrain( gentity_t *self )
  1771. {
  1772. if ( self->health <= 0 )
  1773. {
  1774. return;
  1775. }
  1776. if (self->client->ps.forceHandExtend != HANDEXTEND_NONE)
  1777. {
  1778. return;
  1779. }
  1780. if (self->client->ps.weaponTime > 0)
  1781. {
  1782. return;
  1783. }
  1784. if ( self->client->ps.fd.forcePower < 25 || !WP_ForcePowerUsable( self, FP_DRAIN ) )
  1785. {
  1786. return;
  1787. }
  1788. if ( self->client->ps.fd.forcePowerDebounce[FP_DRAIN] > level.time )
  1789. {//stops it while using it and also after using it, up to 3 second delay
  1790. return;
  1791. }
  1792. // self->client->ps.forceHandExtend = HANDEXTEND_FORCEPUSH;
  1793. // self->client->ps.forceHandExtendTime = level.time + 1000;
  1794. self->client->ps.forceHandExtend = HANDEXTEND_FORCE_HOLD;
  1795. self->client->ps.forceHandExtendTime = level.time + 20000;
  1796. G_Sound( self, CHAN_BODY, G_SoundIndex("sound/weapons/force/drain.wav") );
  1797. WP_ForcePowerStart( self, FP_DRAIN, 500 );
  1798. }
  1799. void ForceDrainDamage( gentity_t *self, gentity_t *traceEnt, vec3_t dir, vec3_t impactPoint )
  1800. {
  1801. gentity_t *tent;
  1802. self->client->dangerTime = level.time;
  1803. self->client->ps.eFlags &= ~EF_INVULNERABLE;
  1804. self->client->invulnerableTimer = 0;
  1805. if ( traceEnt && traceEnt->takedamage )
  1806. {
  1807. if ( traceEnt->client && (!OnSameTeam(self, traceEnt) || g_friendlyFire.integer) && self->client->ps.fd.forceDrainTime < level.time && traceEnt->client->ps.fd.forcePower )
  1808. {//an enemy or object
  1809. if (!traceEnt->client && traceEnt->s.eType == ET_NPC)
  1810. { //g2animent
  1811. if (traceEnt->s.genericenemyindex < level.time)
  1812. {
  1813. traceEnt->s.genericenemyindex = level.time + 2000;
  1814. }
  1815. }
  1816. if (ForcePowerUsableOn(self, traceEnt, FP_DRAIN))
  1817. {
  1818. int modPowerLevel = -1;
  1819. int dmg = 0; //Q_irand( 1, 3 );
  1820. if (self->client->ps.fd.forcePowerLevel[FP_DRAIN] == FORCE_LEVEL_1)
  1821. {
  1822. dmg = 2; //because it's one-shot
  1823. }
  1824. else if (self->client->ps.fd.forcePowerLevel[FP_DRAIN] == FORCE_LEVEL_2)
  1825. {
  1826. dmg = 3;
  1827. }
  1828. else if (self->client->ps.fd.forcePowerLevel[FP_DRAIN] == FORCE_LEVEL_3)
  1829. {
  1830. dmg = 4;
  1831. }
  1832. if (traceEnt->client)
  1833. {
  1834. modPowerLevel = WP_AbsorbConversion(traceEnt, traceEnt->client->ps.fd.forcePowerLevel[FP_ABSORB], self, FP_DRAIN, self->client->ps.fd.forcePowerLevel[FP_DRAIN], 1);
  1835. }
  1836. if (modPowerLevel != -1)
  1837. {
  1838. if (!modPowerLevel)
  1839. {
  1840. dmg = 0;
  1841. }
  1842. else if (modPowerLevel == 1)
  1843. {
  1844. dmg = 1;
  1845. }
  1846. else if (modPowerLevel == 2)
  1847. {
  1848. dmg = 2;
  1849. }
  1850. }
  1851. //G_Damage( traceEnt, self, self, dir, impactPoint, dmg, 0, MOD_FORCE_DARK );
  1852. if (dmg)
  1853. {
  1854. traceEnt->client->ps.fd.forcePower -= (dmg);
  1855. }
  1856. if (traceEnt->client->ps.fd.forcePower < 0)
  1857. {
  1858. traceEnt->client->ps.fd.forcePower = 0;
  1859. }
  1860. if (self->client->ps.stats[STAT_HEALTH] < self->client->ps.stats[STAT_MAX_HEALTH] &&
  1861. self->health > 0 && self->client->ps.stats[STAT_HEALTH] > 0)
  1862. {
  1863. self->health += dmg;
  1864. if (self->health > self->client->ps.stats[STAT_MAX_HEALTH])
  1865. {
  1866. self->health = self->client->ps.stats[STAT_MAX_HEALTH];
  1867. }
  1868. self->client->ps.stats[STAT_HEALTH] = self->health;
  1869. }
  1870. traceEnt->client->ps.fd.forcePowerRegenDebounceTime = level.time + 800; //don't let the client being drained get force power back right away
  1871. //Drain the standard amount since we just drained someone else
  1872. /*
  1873. if (self->client->ps.fd.forcePowerLevel[FP_DRAIN] == FORCE_LEVEL_1)
  1874. {
  1875. BG_ForcePowerDrain( &self->client->ps, FP_DRAIN, 0 );
  1876. }
  1877. else
  1878. {
  1879. BG_ForcePowerDrain( &self->client->ps, FP_DRAIN, forcePowerNeeded[self->client->ps.fd.forcePowerLevel[FP_DRAIN]][FP_DRAIN]/5 );
  1880. }
  1881. if (self->client->ps.fd.forcePowerLevel[FP_DRAIN] == FORCE_LEVEL_1)
  1882. {
  1883. self->client->ps.fd.forceDrainTime = level.time + 100;
  1884. }
  1885. else
  1886. {
  1887. self->client->ps.fd.forceDrainTime = level.time + 20;
  1888. }
  1889. if ( traceEnt->client )
  1890. {
  1891. if ( !Q_irand( 0, 2 ) )
  1892. {
  1893. //G_Sound( traceEnt, CHAN_BODY, G_SoundIndex( "sound/weapons/force/lightninghit.wav" ) );
  1894. }
  1895. // traceEnt->s.powerups |= ( 1 << PW_DISINT_1 );
  1896. // traceEnt->client->ps.powerups[PW_DISINT_1] = level.time + 500;
  1897. }
  1898. */
  1899. if (traceEnt->client->forcePowerSoundDebounce < level.time)
  1900. {
  1901. tent = G_TempEntity( impactPoint, EV_FORCE_DRAINED);
  1902. tent->s.eventParm = DirToByte(dir);
  1903. tent->s.owner = traceEnt->s.number;
  1904. traceEnt->client->forcePowerSoundDebounce = level.time + 400;
  1905. }
  1906. }
  1907. }
  1908. }
  1909. }
  1910. int ForceShootDrain( gentity_t *self )
  1911. {
  1912. trace_t tr;
  1913. vec3_t end, forward;
  1914. gentity_t *traceEnt;
  1915. int gotOneOrMore = 0;
  1916. if ( self->health <= 0 )
  1917. {
  1918. return 0;
  1919. }
  1920. AngleVectors( self->client->ps.viewangles, forward, NULL, NULL );
  1921. VectorNormalize( forward );
  1922. if ( self->client->ps.fd.forcePowerLevel[FP_DRAIN] > FORCE_LEVEL_2 )
  1923. {//arc
  1924. vec3_t center, mins, maxs, dir, ent_org, size, v;
  1925. float radius = MAX_DRAIN_DISTANCE, dot, dist;
  1926. gentity_t *entityList[MAX_GENTITIES];
  1927. int iEntityList[MAX_GENTITIES];
  1928. int e, numListedEntities, i;
  1929. VectorCopy( self->client->ps.origin, center );
  1930. for ( i = 0 ; i < 3 ; i++ )
  1931. {
  1932. mins[i] = center[i] - radius;
  1933. maxs[i] = center[i] + radius;
  1934. }
  1935. numListedEntities = trap_EntitiesInBox( mins, maxs, iEntityList, MAX_GENTITIES );
  1936. i = 0;
  1937. while (i < numListedEntities)
  1938. {
  1939. entityList[i] = &g_entities[iEntityList[i]];
  1940. i++;
  1941. }
  1942. for ( e = 0 ; e < numListedEntities ; e++ )
  1943. {
  1944. traceEnt = entityList[e];
  1945. if ( !traceEnt )
  1946. continue;
  1947. if ( traceEnt == self )
  1948. continue;
  1949. if ( !traceEnt->inuse )
  1950. continue;
  1951. if ( !traceEnt->takedamage )
  1952. continue;
  1953. if ( traceEnt->health <= 0 )//no torturing corpses
  1954. continue;
  1955. if ( !traceEnt->client )
  1956. continue;
  1957. if ( !traceEnt->client->ps.fd.forcePower )
  1958. continue;
  1959. if (OnSameTeam(self, traceEnt) && !g_friendlyFire.integer)
  1960. continue;
  1961. //this is all to see if we need to start a saber attack, if it's in flight, this doesn't matter
  1962. // find the distance from the edge of the bounding box
  1963. for ( i = 0 ; i < 3 ; i++ )
  1964. {
  1965. if ( center[i] < traceEnt->r.absmin[i] )
  1966. {
  1967. v[i] = traceEnt->r.absmin[i] - center[i];
  1968. } else if ( center[i] > traceEnt->r.absmax[i] )
  1969. {
  1970. v[i] = center[i] - traceEnt->r.absmax[i];
  1971. } else
  1972. {
  1973. v[i] = 0;
  1974. }
  1975. }
  1976. VectorSubtract( traceEnt->r.absmax, traceEnt->r.absmin, size );
  1977. VectorMA( traceEnt->r.absmin, 0.5, size, ent_org );
  1978. //see if they're in front of me
  1979. //must be within the forward cone
  1980. VectorSubtract( ent_org, center, dir );
  1981. VectorNormalize( dir );
  1982. if ( (dot = DotProduct( dir, forward )) < 0.5 )
  1983. continue;
  1984. //must be close enough
  1985. dist = VectorLength( v );
  1986. if ( dist >= radius )
  1987. {
  1988. continue;
  1989. }
  1990. //in PVS?
  1991. if ( !traceEnt->r.bmodel && !trap_InPVS( ent_org, self->client->ps.origin ) )
  1992. {//must be in PVS
  1993. continue;
  1994. }
  1995. //Now check and see if we can actually hit it
  1996. trap_Trace( &tr, self->client->ps.origin, vec3_origin, vec3_origin, ent_org, self->s.number, MASK_SHOT );
  1997. if ( tr.fraction < 1.0f && tr.entityNum != traceEnt->s.number )
  1998. {//must have clear LOS
  1999. continue;
  2000. }
  2001. // ok, we are within the radius, add us to the incoming list
  2002. ForceDrainDamage( self, traceEnt, dir, ent_org );
  2003. gotOneOrMore = 1;
  2004. }
  2005. }
  2006. else
  2007. {//trace-line
  2008. VectorMA( self->client->ps.origin, 2048, forward, end );
  2009. trap_Trace( &tr, self->client->ps.origin, vec3_origin, vec3_origin, end, self->s.number, MASK_SHOT );
  2010. if ( tr.entityNum == ENTITYNUM_NONE || tr.fraction == 1.0 || tr.allsolid || tr.startsolid || !g_entities[tr.entityNum].client || !g_entities[tr.entityNum].inuse )
  2011. {
  2012. return 0;
  2013. }
  2014. traceEnt = &g_entities[tr.entityNum];
  2015. ForceDrainDamage( self, traceEnt, forward, tr.endpos );
  2016. gotOneOrMore = 1;
  2017. }
  2018. self->client->ps.activeForcePass = self->client->ps.fd.forcePowerLevel[FP_DRAIN] + FORCE_LEVEL_3;
  2019. BG_ForcePowerDrain( &self->client->ps, FP_DRAIN, 5 ); //used to be 1, but this did, too, anger the God of Balance.
  2020. self->client->ps.fd.forcePowerRegenDebounceTime = level.time + 500;
  2021. return gotOneOrMore;
  2022. }
  2023. void ForceJumpCharge( gentity_t *self, usercmd_t *ucmd )
  2024. { //I guess this is unused now. Was used for the "charge" jump type.
  2025. float forceJumpChargeInterval = forceJumpStrength[0] / (FORCE_JUMP_CHARGE_TIME/FRAMETIME);
  2026. if ( self->health <= 0 )
  2027. {
  2028. return;
  2029. }
  2030. if (!self->client->ps.fd.forceJumpCharge && self->client->ps.groundEntityNum == ENTITYNUM_NONE)
  2031. {
  2032. return;
  2033. }
  2034. if (self->client->ps.fd.forcePower < forcePowerNeeded[self->client->ps.fd.forcePowerLevel[FP_LEVITATION]][FP_LEVITATION])
  2035. {
  2036. G_MuteSound(self->client->ps.fd.killSoundEntIndex[TRACK_CHANNEL_1-50], CHAN_VOICE);
  2037. return;
  2038. }
  2039. if (!self->client->ps.fd.forceJumpCharge)
  2040. {
  2041. self->client->ps.fd.forceJumpAddTime = 0;
  2042. }
  2043. if (self->client->ps.fd.forceJumpAddTime >= level.time)
  2044. {
  2045. return;
  2046. }
  2047. //need to play sound
  2048. if ( !self->client->ps.fd.forceJumpCharge )
  2049. {
  2050. G_Sound( self, TRACK_CHANNEL_1, G_SoundIndex("sound/weapons/force/jumpbuild.wav") );
  2051. }
  2052. //Increment
  2053. if (self->client->ps.fd.forceJumpAddTime < level.time)
  2054. {
  2055. self->client->ps.fd.forceJumpCharge += forceJumpChargeInterval*50;
  2056. self->client->ps.fd.forceJumpAddTime = level.time + 500;
  2057. }
  2058. //clamp to max strength for current level
  2059. if ( self->client->ps.fd.forceJumpCharge > forceJumpStrength[self->client->ps.fd.forcePowerLevel[FP_LEVITATION]] )
  2060. {
  2061. self->client->ps.fd.forceJumpCharge = forceJumpStrength[self->client->ps.fd.forcePowerLevel[FP_LEVITATION]];
  2062. G_MuteSound(self->client->ps.fd.killSoundEntIndex[TRACK_CHANNEL_1-50], CHAN_VOICE);
  2063. }
  2064. //clamp to max available force power
  2065. if ( self->client->ps.fd.forceJumpCharge/forceJumpChargeInterval/(FORCE_JUMP_CHARGE_TIME/FRAMETIME)*forcePowerNeeded[self->client->ps.fd.forcePowerLevel[FP_LEVITATION]][FP_LEVITATION] > self->client->ps.fd.forcePower )
  2066. {//can't use more than you have
  2067. G_MuteSound(self->client->ps.fd.killSoundEntIndex[TRACK_CHANNEL_1-50], CHAN_VOICE);
  2068. self->client->ps.fd.forceJumpCharge = self->client->ps.fd.forcePower*forceJumpChargeInterval/(FORCE_JUMP_CHARGE_TIME/FRAMETIME);
  2069. }
  2070. //G_Printf("%f\n", self->client->ps.fd.forceJumpCharge);
  2071. }
  2072. int WP_GetVelocityForForceJump( gentity_t *self, vec3_t jumpVel, usercmd_t *ucmd )
  2073. {
  2074. float pushFwd = 0, pushRt = 0;
  2075. vec3_t view, forward, right;
  2076. VectorCopy( self->client->ps.viewangles, view );
  2077. view[0] = 0;
  2078. AngleVectors( view, forward, right, NULL );
  2079. if ( ucmd->forwardmove && ucmd->rightmove )
  2080. {
  2081. if ( ucmd->forwardmove > 0 )
  2082. {
  2083. pushFwd = 50;
  2084. }
  2085. else
  2086. {
  2087. pushFwd = -50;
  2088. }
  2089. if ( ucmd->rightmove > 0 )
  2090. {
  2091. pushRt = 50;
  2092. }
  2093. else
  2094. {
  2095. pushRt = -50;
  2096. }
  2097. }
  2098. else if ( ucmd->forwardmove || ucmd->rightmove )
  2099. {
  2100. if ( ucmd->forwardmove > 0 )
  2101. {
  2102. pushFwd = 100;
  2103. }
  2104. else if ( ucmd->forwardmove < 0 )
  2105. {
  2106. pushFwd = -100;
  2107. }
  2108. else if ( ucmd->rightmove > 0 )
  2109. {
  2110. pushRt = 100;
  2111. }
  2112. else if ( ucmd->rightmove < 0 )
  2113. {
  2114. pushRt = -100;
  2115. }
  2116. }
  2117. G_MuteSound(self->client->ps.fd.killSoundEntIndex[TRACK_CHANNEL_1-50], CHAN_VOICE);
  2118. G_PreDefSound(self->client->ps.origin, PDSOUND_FORCEJUMP);
  2119. if (self->client->ps.fd.forceJumpCharge < JUMP_VELOCITY+40)
  2120. { //give him at least a tiny boost from just a tap
  2121. self->client->ps.fd.forceJumpCharge = JUMP_VELOCITY+400;
  2122. }
  2123. if (self->client->ps.velocity[2] < -30)
  2124. { //so that we can get a good boost when force jumping in a fall
  2125. self->client->ps.velocity[2] = -30;
  2126. }
  2127. VectorMA( self->client->ps.velocity, pushFwd, forward, jumpVel );
  2128. VectorMA( self->client->ps.velocity, pushRt, right, jumpVel );
  2129. jumpVel[2] += self->client->ps.fd.forceJumpCharge;
  2130. if ( pushFwd > 0 && self->client->ps.fd.forceJumpCharge > 200 )
  2131. {
  2132. return FJ_FORWARD;
  2133. }
  2134. else if ( pushFwd < 0 && self->client->ps.fd.forceJumpCharge > 200 )
  2135. {
  2136. return FJ_BACKWARD;
  2137. }
  2138. else if ( pushRt > 0 && self->client->ps.fd.forceJumpCharge > 200 )
  2139. {
  2140. return FJ_RIGHT;
  2141. }
  2142. else if ( pushRt < 0 && self->client->ps.fd.forceJumpCharge > 200 )
  2143. {
  2144. return FJ_LEFT;
  2145. }
  2146. else
  2147. {
  2148. return FJ_UP;
  2149. }
  2150. }
  2151. void ForceJump( gentity_t *self, usercmd_t *ucmd )
  2152. {
  2153. float forceJumpChargeInterval;
  2154. vec3_t jumpVel;
  2155. if ( self->client->ps.fd.forcePowerDuration[FP_LEVITATION] > level.time )
  2156. {
  2157. return;
  2158. }
  2159. if ( !WP_ForcePowerUsable( self, FP_LEVITATION ) )
  2160. {
  2161. return;
  2162. }
  2163. if ( self->s.groundEntityNum == ENTITYNUM_NONE )
  2164. {
  2165. return;
  2166. }
  2167. if ( self->health <= 0 )
  2168. {
  2169. return;
  2170. }
  2171. self->client->fjDidJump = qtrue;
  2172. forceJumpChargeInterval = forceJumpStrength[self->client->ps.fd.forcePowerLevel[FP_LEVITATION]]/(FORCE_JUMP_CHARGE_TIME/FRAMETIME);
  2173. WP_GetVelocityForForceJump( self, jumpVel, ucmd );
  2174. //FIXME: sound effect
  2175. self->client->ps.fd.forceJumpZStart = self->client->ps.origin[2];//remember this for when we land
  2176. VectorCopy( jumpVel, self->client->ps.velocity );
  2177. //wasn't allowing them to attack when jumping, but that was annoying
  2178. //self->client->ps.weaponTime = self->client->ps.torsoAnimTimer;
  2179. WP_ForcePowerStart( self, FP_LEVITATION, self->client->ps.fd.forceJumpCharge/forceJumpChargeInterval/(FORCE_JUMP_CHARGE_TIME/FRAMETIME)*forcePowerNeeded[self->client->ps.fd.forcePowerLevel[FP_LEVITATION]][FP_LEVITATION] );
  2180. //self->client->ps.fd.forcePowerDuration[FP_LEVITATION] = level.time + self->client->ps.weaponTime;
  2181. self->client->ps.fd.forceJumpCharge = 0;
  2182. self->client->ps.forceJumpFlip = qtrue;
  2183. // We've just jumped, we're not gonna be on the ground in the following frames.
  2184. // This makes sure npc's wont trigger the ForceJump function multiple times before detecting that they have left the ground.
  2185. self->client->ps.groundEntityNum = ENTITYNUM_NONE;
  2186. }
  2187. void WP_AddAsMindtricked(forcedata_t *fd, int entNum)
  2188. {
  2189. if (!fd)
  2190. {
  2191. return;
  2192. }
  2193. if (entNum > 47)
  2194. {
  2195. fd->forceMindtrickTargetIndex4 |= (1 << (entNum-48));
  2196. }
  2197. else if (entNum > 31)
  2198. {
  2199. fd->forceMindtrickTargetIndex3 |= (1 << (entNum-32));
  2200. }
  2201. else if (entNum > 15)
  2202. {
  2203. fd->forceMindtrickTargetIndex2 |= (1 << (entNum-16));
  2204. }
  2205. else
  2206. {
  2207. fd->forceMindtrickTargetIndex |= (1 << entNum);
  2208. }
  2209. }
  2210. qboolean ForceTelepathyCheckDirectNPCTarget( gentity_t *self, trace_t *tr, qboolean *tookPower )
  2211. {
  2212. gentity_t *traceEnt;
  2213. qboolean targetLive = qfalse, mindTrickDone = qfalse;
  2214. vec3_t tfrom, tto, fwd;
  2215. float radius = MAX_TRICK_DISTANCE;
  2216. //Check for a direct usage on NPCs first
  2217. VectorCopy(self->client->ps.origin, tfrom);
  2218. tfrom[2] += self->client->ps.viewheight;
  2219. AngleVectors(self->client->ps.viewangles, fwd, NULL, NULL);
  2220. tto[0] = tfrom[0] + fwd[0]*radius/2;
  2221. tto[1] = tfrom[1] + fwd[1]*radius/2;
  2222. tto[2] = tfrom[2] + fwd[2]*radius/2;
  2223. trap_Trace( tr, tfrom, NULL, NULL, tto, self->s.number, MASK_PLAYERSOLID );
  2224. if ( tr->entityNum == ENTITYNUM_NONE
  2225. || tr->fraction == 1.0f
  2226. || tr->allsolid
  2227. || tr->startsolid )
  2228. {
  2229. return qfalse;
  2230. }
  2231. traceEnt = &g_entities[tr->entityNum];
  2232. if( traceEnt->NPC
  2233. && traceEnt->NPC->scriptFlags & SCF_NO_FORCE )
  2234. {
  2235. return qfalse;
  2236. }
  2237. if ( traceEnt && traceEnt->client )
  2238. {
  2239. switch ( traceEnt->client->NPC_class )
  2240. {
  2241. case CLASS_GALAKMECH://cant grip him, he's in armor
  2242. case CLASS_ATST://much too big to grip!
  2243. //no droids either
  2244. case CLASS_PROBE:
  2245. case CLASS_GONK:
  2246. case CLASS_R2D2:
  2247. case CLASS_R5D2:
  2248. case CLASS_MARK1:
  2249. case CLASS_MARK2:
  2250. case CLASS_MOUSE:
  2251. case CLASS_SEEKER:
  2252. case CLASS_REMOTE:
  2253. case CLASS_PROTOCOL:
  2254. case CLASS_BOBAFETT:
  2255. case CLASS_RANCOR:
  2256. break;
  2257. default:
  2258. targetLive = qtrue;
  2259. break;
  2260. }
  2261. }
  2262. if ( traceEnt->s.number < MAX_CLIENTS )
  2263. {//a regular client
  2264. return qfalse;
  2265. }
  2266. if ( targetLive && traceEnt->NPC )
  2267. {//hit an organic non-player
  2268. vec3_t eyeDir;
  2269. if ( G_ActivateBehavior( traceEnt, BSET_MINDTRICK ) )
  2270. {//activated a script on him
  2271. //FIXME: do the visual sparkles effect on their heads, still?
  2272. WP_ForcePowerStart( self, FP_TELEPATHY, 0 );
  2273. }
  2274. else if ( (self->NPC && traceEnt->client->playerTeam != self->client->playerTeam)
  2275. || (!self->NPC && traceEnt->client->playerTeam != self->client->sess.sessionTeam) )
  2276. {//an enemy
  2277. int override = 0;
  2278. if ( (traceEnt->NPC->scriptFlags&SCF_NO_MIND_TRICK) )
  2279. {
  2280. }
  2281. else if ( traceEnt->s.weapon != WP_SABER
  2282. && traceEnt->client->NPC_class != CLASS_REBORN )
  2283. {//haha! Jedi aren't easily confused!
  2284. if ( self->client->ps.fd.forcePowerLevel[FP_TELEPATHY] > FORCE_LEVEL_2 )
  2285. {//turn them to our side
  2286. //if mind trick 3 and aiming at an enemy need more force power
  2287. if ( traceEnt->s.weapon != WP_NONE )
  2288. {//don't charm people who aren't capable of fighting... like ugnaughts and droids
  2289. int newPlayerTeam, newEnemyTeam;
  2290. if ( traceEnt->enemy )
  2291. {
  2292. G_ClearEnemy( traceEnt );
  2293. }
  2294. if ( traceEnt->NPC )
  2295. {
  2296. //traceEnt->NPC->tempBehavior = BS_FOLLOW_LEADER;
  2297. traceEnt->client->leader = self;
  2298. }
  2299. //FIXME: maybe pick an enemy right here?
  2300. if ( self->NPC )
  2301. {//NPC
  2302. newPlayerTeam = self->client->playerTeam;
  2303. newEnemyTeam = self->client->enemyTeam;
  2304. }
  2305. else
  2306. {//client/bot
  2307. if ( self->client->sess.sessionTeam == TEAM_BLUE )
  2308. {//rebel
  2309. newPlayerTeam = NPCTEAM_PLAYER;
  2310. newEnemyTeam = NPCTEAM_ENEMY;
  2311. }
  2312. else if ( self->client->sess.sessionTeam == TEAM_RED )
  2313. {//imperial
  2314. newPlayerTeam = NPCTEAM_ENEMY;
  2315. newEnemyTeam = NPCTEAM_PLAYER;
  2316. }
  2317. else
  2318. {//neutral - wan't attack anyone
  2319. newPlayerTeam = NPCTEAM_NEUTRAL;
  2320. newEnemyTeam = NPCTEAM_NEUTRAL;
  2321. }
  2322. }
  2323. //store these for retrieval later
  2324. traceEnt->genericValue1 = traceEnt->client->playerTeam;
  2325. traceEnt->genericValue2 = traceEnt->client->enemyTeam;
  2326. traceEnt->genericValue3 = traceEnt->s.teamowner;
  2327. //set the new values
  2328. traceEnt->client->playerTeam = newPlayerTeam;
  2329. traceEnt->client->enemyTeam = newEnemyTeam;
  2330. traceEnt->s.teamowner = newPlayerTeam;
  2331. //FIXME: need a *charmed* timer on this...? Or do TEAM_PLAYERS assume that "confusion" means they should switch to team_enemy when done?
  2332. traceEnt->NPC->charmedTime = level.time + mindTrickTime[self->client->ps.fd.forcePowerLevel[FP_TELEPATHY]];
  2333. }
  2334. }
  2335. else
  2336. {//just confuse them
  2337. //somehow confuse them? Set don't fire to true for a while? Drop their aggression? Maybe just take their enemy away and don't let them pick one up for a while unless shot?
  2338. traceEnt->NPC->confusionTime = level.time + mindTrickTime[self->client->ps.fd.forcePowerLevel[FP_TELEPATHY]];//confused for about 10 seconds
  2339. NPC_PlayConfusionSound( traceEnt );
  2340. if ( traceEnt->enemy )
  2341. {
  2342. G_ClearEnemy( traceEnt );
  2343. }
  2344. }
  2345. }
  2346. else
  2347. {
  2348. NPC_Jedi_PlayConfusionSound( traceEnt );
  2349. }
  2350. WP_ForcePowerStart( self, FP_TELEPATHY, override );
  2351. }
  2352. else if ( traceEnt->client->playerTeam == self->client->playerTeam )
  2353. {//an ally
  2354. //maybe just have him look at you? Respond? Take your enemy?
  2355. if ( traceEnt->client->ps.pm_type < PM_DEAD && traceEnt->NPC!=NULL && !(traceEnt->NPC->scriptFlags&SCF_NO_RESPONSE) )
  2356. {
  2357. NPC_UseResponse( traceEnt, self, qfalse );
  2358. WP_ForcePowerStart( self, FP_TELEPATHY, 1 );
  2359. }
  2360. }//NOTE: no effect on TEAM_NEUTRAL?
  2361. AngleVectors( traceEnt->client->renderInfo.eyeAngles, eyeDir, NULL, NULL );
  2362. VectorNormalize( eyeDir );
  2363. G_PlayEffectID( G_EffectIndex( "force/force_touch" ), traceEnt->client->renderInfo.eyePoint, eyeDir );
  2364. //make sure this plays and that you cannot press fire for about 1 second after this
  2365. //FIXME: BOTH_FORCEMINDTRICK or BOTH_FORCEDISTRACT
  2366. //NPC_SetAnim( self, SETANIM_TORSO, BOTH_MINDTRICK1, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_RESTART|SETANIM_FLAG_HOLD );
  2367. //FIXME: build-up or delay this until in proper part of anim
  2368. mindTrickDone = qtrue;
  2369. }
  2370. else
  2371. {
  2372. if ( self->client->ps.fd.forcePowerLevel[FP_TELEPATHY] > FORCE_LEVEL_1 && tr->fraction * 2048 > 64 )
  2373. {//don't create a diversion less than 64 from you of if at power level 1
  2374. //use distraction anim instead
  2375. G_PlayEffectID( G_EffectIndex( "force/force_touch" ), tr->endpos, tr->plane.normal );
  2376. //FIXME: these events don't seem to always be picked up...?
  2377. AddSoundEvent( self, tr->endpos, 512, AEL_SUSPICIOUS, qtrue );//, qtrue );
  2378. AddSightEvent( self, tr->endpos, 512, AEL_SUSPICIOUS, 50 );
  2379. WP_ForcePowerStart( self, FP_TELEPATHY, 0 );
  2380. *tookPower = qtrue;
  2381. }
  2382. //NPC_SetAnim( self, SETANIM_TORSO, BOTH_MINDTRICK2, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_RESTART|SETANIM_FLAG_HOLD );
  2383. }
  2384. //self->client->ps.saberMove = self->client->ps.saberBounceMove = LS_READY;//don't finish whatever saber anim you may have been in
  2385. self->client->ps.saberBlocked = BLOCKED_NONE;
  2386. self->client->ps.weaponTime = 1000;
  2387. /*
  2388. if ( self->client->ps.fd.forcePowersActive&(1<<FP_SPEED) )
  2389. {
  2390. self->client->ps.weaponTime = floor( self->client->ps.weaponTime * g_timescale->value );
  2391. }
  2392. */
  2393. return qtrue;
  2394. }
  2395. void ForceTelepathy(gentity_t *self)
  2396. {
  2397. trace_t tr;
  2398. vec3_t tto, thispush_org, a;
  2399. vec3_t mins, maxs, fwdangles, forward, right, center;
  2400. int i;
  2401. float visionArc = 0;
  2402. float radius = MAX_TRICK_DISTANCE;
  2403. qboolean tookPower = qfalse;
  2404. if ( self->health <= 0 )
  2405. {
  2406. return;
  2407. }
  2408. if (self->client->ps.forceHandExtend != HANDEXTEND_NONE)
  2409. {
  2410. return;
  2411. }
  2412. if (self->client->ps.weaponTime > 0)
  2413. {
  2414. return;
  2415. }
  2416. if (self->client->ps.powerups[PW_REDFLAG] ||
  2417. self->client->ps.powerups[PW_BLUEFLAG])
  2418. { //can't mindtrick while carrying the flag
  2419. return;
  2420. }
  2421. if (self->client->ps.forceAllowDeactivateTime < level.time &&
  2422. (self->client->ps.fd.forcePowersActive & (1 << FP_TELEPATHY)) )
  2423. {
  2424. WP_ForcePowerStop( self, FP_TELEPATHY );
  2425. return;
  2426. }
  2427. if ( !WP_ForcePowerUsable( self, FP_TELEPATHY ) )
  2428. {
  2429. return;
  2430. }
  2431. // fix: rocket lock bug
  2432. BG_ClearRocketLock(&self->client->ps);
  2433. if ( ForceTelepathyCheckDirectNPCTarget( self, &tr, &tookPower ) )
  2434. {//hit an NPC directly
  2435. self->client->ps.forceAllowDeactivateTime = level.time + 1500;
  2436. G_Sound( self, CHAN_AUTO, G_SoundIndex("sound/weapons/force/distract.wav") );
  2437. self->client->ps.forceHandExtend = HANDEXTEND_FORCEPUSH;
  2438. self->client->ps.forceHandExtendTime = level.time + 1000;
  2439. return;
  2440. }
  2441. if (self->client->ps.fd.forcePowerLevel[FP_TELEPATHY] == FORCE_LEVEL_2)
  2442. {
  2443. visionArc = 180;
  2444. }
  2445. else if (self->client->ps.fd.forcePowerLevel[FP_TELEPATHY] == FORCE_LEVEL_3)
  2446. {
  2447. visionArc = 360;
  2448. radius = MAX_TRICK_DISTANCE*2.0f;
  2449. }
  2450. VectorCopy( self->client->ps.viewangles, fwdangles );
  2451. AngleVectors( fwdangles, forward, right, NULL );
  2452. VectorCopy( self->client->ps.origin, center );
  2453. for ( i = 0 ; i < 3 ; i++ )
  2454. {
  2455. mins[i] = center[i] - radius;
  2456. maxs[i] = center[i] + radius;
  2457. }
  2458. if (self->client->ps.fd.forcePowerLevel[FP_TELEPATHY] == FORCE_LEVEL_1)
  2459. {
  2460. if (tr.fraction != 1.0 &&
  2461. tr.entityNum != ENTITYNUM_NONE &&
  2462. g_entities[tr.entityNum].inuse &&
  2463. g_entities[tr.entityNum].client &&
  2464. g_entities[tr.entityNum].client->pers.connected &&
  2465. g_entities[tr.entityNum].client->sess.sessionTeam != TEAM_SPECTATOR)
  2466. {
  2467. WP_AddAsMindtricked(&self->client->ps.fd, tr.entityNum);
  2468. if ( !tookPower )
  2469. {
  2470. WP_ForcePowerStart( self, FP_TELEPATHY, 0 );
  2471. }
  2472. G_Sound( self, CHAN_AUTO, G_SoundIndex("sound/weapons/force/distract.wav") );
  2473. self->client->ps.forceHandExtend = HANDEXTEND_FORCEPUSH;
  2474. self->client->ps.forceHandExtendTime = level.time + 1000;
  2475. return;
  2476. }
  2477. else
  2478. {
  2479. return;
  2480. }
  2481. }
  2482. else //level 2 & 3
  2483. {
  2484. gentity_t *ent;
  2485. int entityList[MAX_GENTITIES];
  2486. int numListedEntities;
  2487. int e = 0;
  2488. qboolean gotatleastone = qfalse;
  2489. numListedEntities = trap_EntitiesInBox( mins, maxs, entityList, MAX_GENTITIES );
  2490. while (e < numListedEntities)
  2491. {
  2492. ent = &g_entities[entityList[e]];
  2493. if (ent)
  2494. { //not in the arc, don't consider it
  2495. if (ent->client)
  2496. {
  2497. VectorCopy(ent->client->ps.origin, thispush_org);
  2498. }
  2499. else
  2500. {
  2501. VectorCopy(ent->s.pos.trBase, thispush_org);
  2502. }
  2503. VectorCopy(self->client->ps.origin, tto);
  2504. tto[2] += self->client->ps.viewheight;
  2505. VectorSubtract(thispush_org, tto, a);
  2506. vectoangles(a, a);
  2507. if (!ent->client)
  2508. {
  2509. entityList[e] = ENTITYNUM_NONE;
  2510. }
  2511. else if (!InFieldOfVision(self->client->ps.viewangles, visionArc, a))
  2512. { //only bother with arc rules if the victim is a client
  2513. entityList[e] = ENTITYNUM_NONE;
  2514. }
  2515. else if (!ForcePowerUsableOn(self, ent, FP_TELEPATHY))
  2516. {
  2517. entityList[e] = ENTITYNUM_NONE;
  2518. }
  2519. else if (OnSameTeam(self, ent))
  2520. {
  2521. entityList[e] = ENTITYNUM_NONE;
  2522. }
  2523. }
  2524. ent = &g_entities[entityList[e]];
  2525. if (ent && ent != self && ent->client)
  2526. {
  2527. gotatleastone = qtrue;
  2528. WP_AddAsMindtricked(&self->client->ps.fd, ent->s.number);
  2529. }
  2530. e++;
  2531. }
  2532. if (gotatleastone)
  2533. {
  2534. self->client->ps.forceAllowDeactivateTime = level.time + 1500;
  2535. if ( !tookPower )
  2536. {
  2537. WP_ForcePowerStart( self, FP_TELEPATHY, 0 );
  2538. }
  2539. G_Sound( self, CHAN_AUTO, G_SoundIndex("sound/weapons/force/distract.wav") );
  2540. self->client->ps.forceHandExtend = HANDEXTEND_FORCEPUSH;
  2541. self->client->ps.forceHandExtendTime = level.time + 1000;
  2542. }
  2543. }
  2544. }
  2545. void GEntity_UseFunc( gentity_t *self, gentity_t *other, gentity_t *activator )
  2546. {
  2547. GlobalUse(self, other, activator);
  2548. }
  2549. qboolean CanCounterThrow(gentity_t *self, gentity_t *thrower, qboolean pull)
  2550. {
  2551. int powerUse = 0;
  2552. if (self->client->ps.forceHandExtend != HANDEXTEND_NONE)
  2553. {
  2554. return 0;
  2555. }
  2556. if (self->client->ps.weaponTime > 0)
  2557. {
  2558. return 0;
  2559. }
  2560. if ( self->health <= 0 )
  2561. {
  2562. return 0;
  2563. }
  2564. if ( self->client->ps.powerups[PW_DISINT_4] > level.time )
  2565. {
  2566. return 0;
  2567. }
  2568. if (self->client->ps.weaponstate == WEAPON_CHARGING ||
  2569. self->client->ps.weaponstate == WEAPON_CHARGING_ALT)
  2570. { //don't autodefend when charging a weapon
  2571. return 0;
  2572. }
  2573. if (level.gametype == GT_SIEGE &&
  2574. pull &&
  2575. thrower && thrower->client)
  2576. { //in siege, pull will affect people if they are not facing you, so they can't run away so much
  2577. vec3_t d;
  2578. float a;
  2579. VectorSubtract(thrower->client->ps.origin, self->client->ps.origin, d);
  2580. vectoangles(d, d);
  2581. a = AngleSubtract(d[YAW], self->client->ps.viewangles[YAW]);
  2582. if (a > 60.0f || a < -60.0f)
  2583. { //if facing more than 60 degrees away they cannot defend
  2584. return 0;
  2585. }
  2586. }
  2587. if (pull)
  2588. {
  2589. powerUse = FP_PULL;
  2590. }
  2591. else
  2592. {
  2593. powerUse = FP_PUSH;
  2594. }
  2595. if ( !WP_ForcePowerUsable( self, powerUse ) )
  2596. {
  2597. return 0;
  2598. }
  2599. if (self->client->ps.groundEntityNum == ENTITYNUM_NONE)
  2600. { //you cannot counter a push/pull if you're in the air
  2601. return 0;
  2602. }
  2603. return 1;
  2604. }
  2605. qboolean G_InGetUpAnim(playerState_t *ps)
  2606. {
  2607. switch( (ps->legsAnim) )
  2608. {
  2609. case BOTH_GETUP1:
  2610. case BOTH_GETUP2:
  2611. case BOTH_GETUP3:
  2612. case BOTH_GETUP4:
  2613. case BOTH_GETUP5:
  2614. case BOTH_FORCE_GETUP_F1:
  2615. case BOTH_FORCE_GETUP_F2:
  2616. case BOTH_FORCE_GETUP_B1:
  2617. case BOTH_FORCE_GETUP_B2:
  2618. case BOTH_FORCE_GETUP_B3:
  2619. case BOTH_FORCE_GETUP_B4:
  2620. case BOTH_FORCE_GETUP_B5:
  2621. case BOTH_GETUP_BROLL_B:
  2622. case BOTH_GETUP_BROLL_F:
  2623. case BOTH_GETUP_BROLL_L:
  2624. case BOTH_GETUP_BROLL_R:
  2625. case BOTH_GETUP_FROLL_B:
  2626. case BOTH_GETUP_FROLL_F:
  2627. case BOTH_GETUP_FROLL_L:
  2628. case BOTH_GETUP_FROLL_R:
  2629. return qtrue;
  2630. }
  2631. switch( (ps->torsoAnim) )
  2632. {
  2633. case BOTH_GETUP1:
  2634. case BOTH_GETUP2:
  2635. case BOTH_GETUP3:
  2636. case BOTH_GETUP4:
  2637. case BOTH_GETUP5:
  2638. case BOTH_FORCE_GETUP_F1:
  2639. case BOTH_FORCE_GETUP_F2:
  2640. case BOTH_FORCE_GETUP_B1:
  2641. case BOTH_FORCE_GETUP_B2:
  2642. case BOTH_FORCE_GETUP_B3:
  2643. case BOTH_FORCE_GETUP_B4:
  2644. case BOTH_FORCE_GETUP_B5:
  2645. case BOTH_GETUP_BROLL_B:
  2646. case BOTH_GETUP_BROLL_F:
  2647. case BOTH_GETUP_BROLL_L:
  2648. case BOTH_GETUP_BROLL_R:
  2649. case BOTH_GETUP_FROLL_B:
  2650. case BOTH_GETUP_FROLL_F:
  2651. case BOTH_GETUP_FROLL_L:
  2652. case BOTH_GETUP_FROLL_R:
  2653. return qtrue;
  2654. }
  2655. return qfalse;
  2656. }
  2657. void G_LetGoOfWall( gentity_t *ent )
  2658. {
  2659. if ( !ent || !ent->client )
  2660. {
  2661. return;
  2662. }
  2663. ent->client->ps.pm_flags &= ~PMF_STUCK_TO_WALL;
  2664. if ( BG_InReboundJump( ent->client->ps.legsAnim )
  2665. || BG_InReboundHold( ent->client->ps.legsAnim ) )
  2666. {
  2667. ent->client->ps.legsTimer = 0;
  2668. }
  2669. if ( BG_InReboundJump( ent->client->ps.torsoAnim )
  2670. || BG_InReboundHold( ent->client->ps.torsoAnim ) )
  2671. {
  2672. ent->client->ps.torsoTimer = 0;
  2673. }
  2674. }
  2675. float forcePushPullRadius[NUM_FORCE_POWER_LEVELS] =
  2676. {
  2677. 0,//none
  2678. 384,//256,
  2679. 448,//384,
  2680. 512
  2681. };
  2682. //rwwFIXMEFIXME: incorporate this into the below function? Currently it's only being used by jedi AI
  2683. extern void Touch_Button(gentity_t *ent, gentity_t *other, trace_t *trace );
  2684. void ForceThrow( gentity_t *self, qboolean pull )
  2685. {
  2686. //shove things in front of you away
  2687. float dist;
  2688. gentity_t *ent;
  2689. int entityList[MAX_GENTITIES];
  2690. gentity_t *push_list[MAX_GENTITIES];
  2691. int numListedEntities;
  2692. vec3_t mins, maxs;
  2693. vec3_t v;
  2694. int i, e;
  2695. int ent_count = 0;
  2696. int radius = 1024; //since it's view-based now. //350;
  2697. int powerLevel;
  2698. int visionArc;
  2699. int pushPower;
  2700. int pushPowerMod;
  2701. vec3_t center, ent_org, size, forward, right, end, dir, fwdangles = {0};
  2702. float dot1;
  2703. trace_t tr;
  2704. int x;
  2705. vec3_t pushDir;
  2706. vec3_t thispush_org;
  2707. vec3_t tfrom, tto, fwd, a;
  2708. float knockback = pull?0:200;
  2709. int powerUse = 0;
  2710. visionArc = 0;
  2711. if (self->client->ps.forceHandExtend != HANDEXTEND_NONE && (self->client->ps.forceHandExtend != HANDEXTEND_KNOCKDOWN || !G_InGetUpAnim(&self->client->ps)))
  2712. {
  2713. return;
  2714. }
  2715. if (!g_useWhileThrowing.integer && self->client->ps.saberInFlight)
  2716. {
  2717. return;
  2718. }
  2719. if (self->client->ps.weaponTime > 0)
  2720. {
  2721. return;
  2722. }
  2723. if ( self->health <= 0 )
  2724. {
  2725. return;
  2726. }
  2727. if ( self->client->ps.powerups[PW_DISINT_4] > level.time )
  2728. {
  2729. return;
  2730. }
  2731. if (pull)
  2732. {
  2733. powerUse = FP_PULL;
  2734. }
  2735. else
  2736. {
  2737. powerUse = FP_PUSH;
  2738. }
  2739. if ( !WP_ForcePowerUsable( self, powerUse ) )
  2740. {
  2741. return;
  2742. }
  2743. // fix: rocket lock bug
  2744. BG_ClearRocketLock(&self->client->ps);
  2745. if (!pull && self->client->ps.saberLockTime > level.time && self->client->ps.saberLockFrame)
  2746. {
  2747. G_Sound( self, CHAN_BODY, G_SoundIndex( "sound/weapons/force/push.wav" ) );
  2748. self->client->ps.powerups[PW_DISINT_4] = level.time + 1500;
  2749. self->client->ps.saberLockHits += self->client->ps.fd.forcePowerLevel[FP_PUSH]*2;
  2750. WP_ForcePowerStart( self, FP_PUSH, 0 );
  2751. return;
  2752. }
  2753. WP_ForcePowerStart( self, powerUse, 0 );
  2754. //make sure this plays and that you cannot press fire for about 1 second after this
  2755. if ( pull )
  2756. {
  2757. G_Sound( self, CHAN_BODY, G_SoundIndex( "sound/weapons/force/pull.wav" ) );
  2758. if (self->client->ps.forceHandExtend == HANDEXTEND_NONE)
  2759. {
  2760. self->client->ps.forceHandExtend = HANDEXTEND_FORCEPULL;
  2761. if ( level.gametype == GT_SIEGE && self->client->ps.weapon == WP_SABER )
  2762. {//hold less so can attack right after a pull
  2763. self->client->ps.forceHandExtendTime = level.time + 200;
  2764. }
  2765. else
  2766. {
  2767. self->client->ps.forceHandExtendTime = level.time + 400;
  2768. }
  2769. }
  2770. self->client->ps.powerups[PW_DISINT_4] = self->client->ps.forceHandExtendTime + 200;
  2771. self->client->ps.powerups[PW_PULL] = self->client->ps.powerups[PW_DISINT_4];
  2772. }
  2773. else
  2774. {
  2775. G_Sound( self, CHAN_BODY, G_SoundIndex( "sound/weapons/force/push.wav" ) );
  2776. if (self->client->ps.forceHandExtend == HANDEXTEND_NONE)
  2777. {
  2778. self->client->ps.forceHandExtend = HANDEXTEND_FORCEPUSH;
  2779. self->client->ps.forceHandExtendTime = level.time + 1000;
  2780. }
  2781. else if (self->client->ps.forceHandExtend == HANDEXTEND_KNOCKDOWN && G_InGetUpAnim(&self->client->ps))
  2782. {
  2783. if (self->client->ps.forceDodgeAnim > 4)
  2784. {
  2785. self->client->ps.forceDodgeAnim -= 8;
  2786. }
  2787. self->client->ps.forceDodgeAnim += 8; //special case, play push on upper torso, but keep playing current knockdown anim on legs
  2788. }
  2789. self->client->ps.powerups[PW_DISINT_4] = level.time + 1100;
  2790. self->client->ps.powerups[PW_PULL] = 0;
  2791. }
  2792. VectorCopy( self->client->ps.viewangles, fwdangles );
  2793. AngleVectors( fwdangles, forward, right, NULL );
  2794. VectorCopy( self->client->ps.origin, center );
  2795. for ( i = 0 ; i < 3 ; i++ )
  2796. {
  2797. mins[i] = center[i] - radius;
  2798. maxs[i] = center[i] + radius;
  2799. }
  2800. if (pull)
  2801. {
  2802. powerLevel = self->client->ps.fd.forcePowerLevel[FP_PULL];
  2803. pushPower = 256*self->client->ps.fd.forcePowerLevel[FP_PULL];
  2804. }
  2805. else
  2806. {
  2807. powerLevel = self->client->ps.fd.forcePowerLevel[FP_PUSH];
  2808. pushPower = 256*self->client->ps.fd.forcePowerLevel[FP_PUSH];
  2809. }
  2810. if (!powerLevel)
  2811. { //Shouldn't have made it here..
  2812. return;
  2813. }
  2814. if (powerLevel == FORCE_LEVEL_2)
  2815. {
  2816. visionArc = 60;
  2817. }
  2818. else if (powerLevel == FORCE_LEVEL_3)
  2819. {
  2820. visionArc = 180;
  2821. }
  2822. if (powerLevel == FORCE_LEVEL_1)
  2823. { //can only push/pull targeted things at level 1
  2824. VectorCopy(self->client->ps.origin, tfrom);
  2825. tfrom[2] += self->client->ps.viewheight;
  2826. AngleVectors(self->client->ps.viewangles, fwd, NULL, NULL);
  2827. tto[0] = tfrom[0] + fwd[0]*radius/2;
  2828. tto[1] = tfrom[1] + fwd[1]*radius/2;
  2829. tto[2] = tfrom[2] + fwd[2]*radius/2;
  2830. trap_Trace(&tr, tfrom, NULL, NULL, tto, self->s.number, MASK_PLAYERSOLID);
  2831. if (tr.fraction != 1.0 &&
  2832. tr.entityNum != ENTITYNUM_NONE)
  2833. {
  2834. if (!g_entities[tr.entityNum].client && g_entities[tr.entityNum].s.eType == ET_NPC)
  2835. { //g2animent
  2836. if (g_entities[tr.entityNum].s.genericenemyindex < level.time)
  2837. {
  2838. g_entities[tr.entityNum].s.genericenemyindex = level.time + 2000;
  2839. }
  2840. }
  2841. numListedEntities = 0;
  2842. entityList[numListedEntities] = tr.entityNum;
  2843. if (pull)
  2844. {
  2845. if (!ForcePowerUsableOn(self, &g_entities[tr.entityNum], FP_PULL))
  2846. {
  2847. return;
  2848. }
  2849. }
  2850. else
  2851. {
  2852. if (!ForcePowerUsableOn(self, &g_entities[tr.entityNum], FP_PUSH))
  2853. {
  2854. return;
  2855. }
  2856. }
  2857. numListedEntities++;
  2858. }
  2859. else
  2860. {
  2861. //didn't get anything, so just
  2862. return;
  2863. }
  2864. }
  2865. else
  2866. {
  2867. numListedEntities = trap_EntitiesInBox( mins, maxs, entityList, MAX_GENTITIES );
  2868. e = 0;
  2869. while (e < numListedEntities)
  2870. {
  2871. ent = &g_entities[entityList[e]];
  2872. if (!ent->client && ent->s.eType == ET_NPC)
  2873. { //g2animent
  2874. if (ent->s.genericenemyindex < level.time)
  2875. {
  2876. ent->s.genericenemyindex = level.time + 2000;
  2877. }
  2878. }
  2879. if (ent)
  2880. {
  2881. if (ent->client)
  2882. {
  2883. VectorCopy(ent->client->ps.origin, thispush_org);
  2884. }
  2885. else
  2886. {
  2887. VectorCopy(ent->s.pos.trBase, thispush_org);
  2888. }
  2889. }
  2890. if (ent)
  2891. { //not in the arc, don't consider it
  2892. VectorCopy(self->client->ps.origin, tto);
  2893. tto[2] += self->client->ps.viewheight;
  2894. VectorSubtract(thispush_org, tto, a);
  2895. vectoangles(a, a);
  2896. if (ent->client && !InFieldOfVision(self->client->ps.viewangles, visionArc, a) &&
  2897. ForcePowerUsableOn(self, ent, powerUse))
  2898. { //only bother with arc rules if the victim is a client
  2899. entityList[e] = ENTITYNUM_NONE;
  2900. }
  2901. else if (ent->client)
  2902. {
  2903. if (pull)
  2904. {
  2905. if (!ForcePowerUsableOn(self, ent, FP_PULL))
  2906. {
  2907. entityList[e] = ENTITYNUM_NONE;
  2908. }
  2909. }
  2910. else
  2911. {
  2912. if (!ForcePowerUsableOn(self, ent, FP_PUSH))
  2913. {
  2914. entityList[e] = ENTITYNUM_NONE;
  2915. }
  2916. }
  2917. }
  2918. }
  2919. e++;
  2920. }
  2921. }
  2922. for ( e = 0 ; e < numListedEntities ; e++ )
  2923. {
  2924. if (entityList[e] != ENTITYNUM_NONE &&
  2925. entityList[e] >= 0 &&
  2926. entityList[e] < MAX_GENTITIES)
  2927. {
  2928. ent = &g_entities[entityList[e]];
  2929. }
  2930. else
  2931. {
  2932. ent = NULL;
  2933. }
  2934. if (!ent)
  2935. continue;
  2936. if (ent == self)
  2937. continue;
  2938. if (ent->client && OnSameTeam(ent, self))
  2939. {
  2940. continue;
  2941. }
  2942. if ( !(ent->inuse) )
  2943. continue;
  2944. if ( ent->s.eType != ET_MISSILE )
  2945. {
  2946. if ( ent->s.eType != ET_ITEM )
  2947. {
  2948. //FIXME: need pushable objects
  2949. if ( Q_stricmp( "func_button", ent->classname ) == 0 )
  2950. {//we might push it
  2951. if ( pull || !(ent->spawnflags&SPF_BUTTON_FPUSHABLE) )
  2952. {//not force-pushable, never pullable
  2953. continue;
  2954. }
  2955. }
  2956. else
  2957. {
  2958. if ( ent->s.eFlags & EF_NODRAW )
  2959. {
  2960. continue;
  2961. }
  2962. if ( !ent->client )
  2963. {
  2964. if ( Q_stricmp( "lightsaber", ent->classname ) != 0 )
  2965. {//not a lightsaber
  2966. if ( Q_stricmp( "func_door", ent->classname ) != 0 || !(ent->spawnflags & 2/*MOVER_FORCE_ACTIVATE*/) )
  2967. {//not a force-usable door
  2968. if ( Q_stricmp( "func_static", ent->classname ) != 0 || (!(ent->spawnflags&1/*F_PUSH*/)&&!(ent->spawnflags&2/*F_PULL*/)) )
  2969. {//not a force-usable func_static
  2970. if ( Q_stricmp( "limb", ent->classname ) )
  2971. {//not a limb
  2972. continue;
  2973. }
  2974. }
  2975. }
  2976. else if ( ent->moverState != MOVER_POS1 && ent->moverState != MOVER_POS2 )
  2977. {//not at rest
  2978. continue;
  2979. }
  2980. }
  2981. }
  2982. else if ( ent->client->NPC_class == CLASS_GALAKMECH
  2983. || ent->client->NPC_class == CLASS_ATST
  2984. || ent->client->NPC_class == CLASS_RANCOR )
  2985. {//can't push ATST or Galak or Rancor
  2986. continue;
  2987. }
  2988. }
  2989. }
  2990. }
  2991. else
  2992. {
  2993. if ( ent->s.pos.trType == TR_STATIONARY && (ent->s.eFlags&EF_MISSILE_STICK) )
  2994. {//can't force-push/pull stuck missiles (detpacks, tripmines)
  2995. continue;
  2996. }
  2997. if ( ent->s.pos.trType == TR_STATIONARY && ent->s.weapon != WP_THERMAL )
  2998. {//only thermal detonators can be pushed once stopped
  2999. continue;
  3000. }
  3001. }
  3002. //this is all to see if we need to start a saber attack, if it's in flight, this doesn't matter
  3003. // find the distance from the edge of the bounding box
  3004. for ( i = 0 ; i < 3 ; i++ )
  3005. {
  3006. if ( center[i] < ent->r.absmin[i] )
  3007. {
  3008. v[i] = ent->r.absmin[i] - center[i];
  3009. } else if ( center[i] > ent->r.absmax[i] )
  3010. {
  3011. v[i] = center[i] - ent->r.absmax[i];
  3012. } else
  3013. {
  3014. v[i] = 0;
  3015. }
  3016. }
  3017. VectorSubtract( ent->r.absmax, ent->r.absmin, size );
  3018. VectorMA( ent->r.absmin, 0.5, size, ent_org );
  3019. VectorSubtract( ent_org, center, dir );
  3020. VectorNormalize( dir );
  3021. if ( (dot1 = DotProduct( dir, forward )) < 0.6 )
  3022. continue;
  3023. dist = VectorLength( v );
  3024. //Now check and see if we can actually deflect it
  3025. //method1
  3026. //if within a certain range, deflect it
  3027. if ( dist >= radius )
  3028. {
  3029. continue;
  3030. }
  3031. //in PVS?
  3032. if ( !ent->r.bmodel && !trap_InPVS( ent_org, self->client->ps.origin ) )
  3033. {//must be in PVS
  3034. continue;
  3035. }
  3036. //really should have a clear LOS to this thing...
  3037. trap_Trace( &tr, self->client->ps.origin, vec3_origin, vec3_origin, ent_org, self->s.number, MASK_SHOT );
  3038. if ( tr.fraction < 1.0f && tr.entityNum != ent->s.number )
  3039. {//must have clear LOS
  3040. //try from eyes too before you give up
  3041. vec3_t eyePoint;
  3042. VectorCopy(self->client->ps.origin, eyePoint);
  3043. eyePoint[2] += self->client->ps.viewheight;
  3044. trap_Trace( &tr, eyePoint, vec3_origin, vec3_origin, ent_org, self->s.number, MASK_SHOT );
  3045. if ( tr.fraction < 1.0f && tr.entityNum != ent->s.number )
  3046. {
  3047. continue;
  3048. }
  3049. }
  3050. // ok, we are within the radius, add us to the incoming list
  3051. push_list[ent_count] = ent;
  3052. ent_count++;
  3053. }
  3054. if ( ent_count )
  3055. {
  3056. //method1:
  3057. for ( x = 0; x < ent_count; x++ )
  3058. {
  3059. int modPowerLevel = powerLevel;
  3060. if (push_list[x]->client)
  3061. {
  3062. modPowerLevel = WP_AbsorbConversion(push_list[x], push_list[x]->client->ps.fd.forcePowerLevel[FP_ABSORB], self, powerUse, powerLevel, forcePowerNeeded[self->client->ps.fd.forcePowerLevel[powerUse]][powerUse]);
  3063. if (modPowerLevel == -1)
  3064. {
  3065. modPowerLevel = powerLevel;
  3066. }
  3067. }
  3068. pushPower = 256*modPowerLevel;
  3069. if (push_list[x]->client)
  3070. {
  3071. VectorCopy(push_list[x]->client->ps.origin, thispush_org);
  3072. }
  3073. else
  3074. {
  3075. VectorCopy(push_list[x]->s.origin, thispush_org);
  3076. }
  3077. if ( push_list[x]->client )
  3078. {//FIXME: make enemy jedi able to hunker down and resist this?
  3079. int otherPushPower = push_list[x]->client->ps.fd.forcePowerLevel[powerUse];
  3080. qboolean canPullWeapon = qtrue;
  3081. float dirLen = 0;
  3082. if ( g_debugMelee.integer )
  3083. {
  3084. if ( (push_list[x]->client->ps.pm_flags&PMF_STUCK_TO_WALL) )
  3085. {//no resistance if stuck to wall
  3086. //push/pull them off the wall
  3087. otherPushPower = 0;
  3088. G_LetGoOfWall( push_list[x] );
  3089. }
  3090. }
  3091. knockback = pull?0:200;
  3092. pushPowerMod = pushPower;
  3093. if (push_list[x]->client->pers.cmd.forwardmove ||
  3094. push_list[x]->client->pers.cmd.rightmove)
  3095. { //if you are moving, you get one less level of defense
  3096. otherPushPower--;
  3097. if (otherPushPower < 0)
  3098. {
  3099. otherPushPower = 0;
  3100. }
  3101. }
  3102. if (otherPushPower && CanCounterThrow(push_list[x], self, pull))
  3103. {
  3104. if ( pull )
  3105. {
  3106. G_Sound( push_list[x], CHAN_BODY, G_SoundIndex( "sound/weapons/force/pull.wav" ) );
  3107. push_list[x]->client->ps.forceHandExtend = HANDEXTEND_FORCEPULL;
  3108. push_list[x]->client->ps.forceHandExtendTime = level.time + 400;
  3109. }
  3110. else
  3111. {
  3112. G_Sound( push_list[x], CHAN_BODY, G_SoundIndex( "sound/weapons/force/push.wav" ) );
  3113. push_list[x]->client->ps.forceHandExtend = HANDEXTEND_FORCEPUSH;
  3114. push_list[x]->client->ps.forceHandExtendTime = level.time + 1000;
  3115. }
  3116. push_list[x]->client->ps.powerups[PW_DISINT_4] = push_list[x]->client->ps.forceHandExtendTime + 200;
  3117. if (pull)
  3118. {
  3119. push_list[x]->client->ps.powerups[PW_PULL] = push_list[x]->client->ps.powerups[PW_DISINT_4];
  3120. }
  3121. else
  3122. {
  3123. push_list[x]->client->ps.powerups[PW_PULL] = 0;
  3124. }
  3125. //Make a counter-throw effect
  3126. if (otherPushPower >= modPowerLevel)
  3127. {
  3128. pushPowerMod = 0;
  3129. canPullWeapon = qfalse;
  3130. }
  3131. else
  3132. {
  3133. int powerDif = (modPowerLevel - otherPushPower);
  3134. if (powerDif >= 3)
  3135. {
  3136. pushPowerMod -= pushPowerMod*0.2;
  3137. }
  3138. else if (powerDif == 2)
  3139. {
  3140. pushPowerMod -= pushPowerMod*0.4;
  3141. }
  3142. else if (powerDif == 1)
  3143. {
  3144. pushPowerMod -= pushPowerMod*0.8;
  3145. }
  3146. if (pushPowerMod < 0)
  3147. {
  3148. pushPowerMod = 0;
  3149. }
  3150. }
  3151. }
  3152. //shove them
  3153. if ( pull )
  3154. {
  3155. VectorSubtract( self->client->ps.origin, thispush_org, pushDir );
  3156. if (push_list[x]->client && VectorLength(pushDir) <= 256)
  3157. {
  3158. int randfact = 0;
  3159. if (modPowerLevel == FORCE_LEVEL_1)
  3160. {
  3161. randfact = 3;
  3162. }
  3163. else if (modPowerLevel == FORCE_LEVEL_2)
  3164. {
  3165. randfact = 7;
  3166. }
  3167. else if (modPowerLevel == FORCE_LEVEL_3)
  3168. {
  3169. randfact = 10;
  3170. }
  3171. if (!OnSameTeam(self, push_list[x]) && Q_irand(1, 10) <= randfact && canPullWeapon)
  3172. {
  3173. vec3_t uorg, vecnorm;
  3174. VectorCopy(self->client->ps.origin, uorg);
  3175. uorg[2] += 64;
  3176. VectorSubtract(uorg, thispush_org, vecnorm);
  3177. VectorNormalize(vecnorm);
  3178. TossClientWeapon(push_list[x], vecnorm, 500);
  3179. }
  3180. }
  3181. }
  3182. else
  3183. {
  3184. VectorSubtract( thispush_org, self->client->ps.origin, pushDir );
  3185. }
  3186. if ((modPowerLevel > otherPushPower || push_list[x]->client->ps.m_iVehicleNum) && push_list[x]->client)
  3187. {
  3188. if (modPowerLevel == FORCE_LEVEL_3 &&
  3189. push_list[x]->client->ps.forceHandExtend != HANDEXTEND_KNOCKDOWN)
  3190. {
  3191. dirLen = VectorLength(pushDir);
  3192. if (BG_KnockDownable(&push_list[x]->client->ps) &&
  3193. dirLen <= (64*((modPowerLevel - otherPushPower)-1)))
  3194. { //can only do a knockdown if fairly close
  3195. push_list[x]->client->ps.forceHandExtend = HANDEXTEND_KNOCKDOWN;
  3196. push_list[x]->client->ps.forceHandExtendTime = level.time + 700;
  3197. push_list[x]->client->ps.forceDodgeAnim = 0; //this toggles between 1 and 0, when it's 1 we should play the get up anim
  3198. push_list[x]->client->ps.quickerGetup = qtrue;
  3199. }
  3200. else if (push_list[x]->s.number < MAX_CLIENTS && push_list[x]->client->ps.m_iVehicleNum &&
  3201. dirLen <= 128.0f )
  3202. { //a player on a vehicle
  3203. gentity_t *vehEnt = &g_entities[push_list[x]->client->ps.m_iVehicleNum];
  3204. if (vehEnt->inuse && vehEnt->client && vehEnt->m_pVehicle)
  3205. {
  3206. if (vehEnt->m_pVehicle->m_pVehicleInfo->type == VH_SPEEDER ||
  3207. vehEnt->m_pVehicle->m_pVehicleInfo->type == VH_ANIMAL)
  3208. { //push the guy off
  3209. vehEnt->m_pVehicle->m_pVehicleInfo->Eject(vehEnt->m_pVehicle, (bgEntity_t *)push_list[x], qfalse);
  3210. }
  3211. }
  3212. }
  3213. }
  3214. }
  3215. if (!dirLen)
  3216. {
  3217. dirLen = VectorLength(pushDir);
  3218. }
  3219. VectorNormalize(pushDir);
  3220. if (push_list[x]->client)
  3221. {
  3222. //escape a force grip if we're in one
  3223. if (self->client->ps.fd.forceGripBeingGripped > level.time)
  3224. { //force the enemy to stop gripping me if I managed to push him
  3225. if (push_list[x]->client->ps.fd.forceGripEntityNum == self->s.number)
  3226. {
  3227. if (modPowerLevel >= push_list[x]->client->ps.fd.forcePowerLevel[FP_GRIP])
  3228. { //only break the grip if our push/pull level is >= their grip level
  3229. WP_ForcePowerStop(push_list[x], FP_GRIP);
  3230. self->client->ps.fd.forceGripBeingGripped = 0;
  3231. push_list[x]->client->ps.fd.forceGripUseTime = level.time + 1000; //since we just broke out of it..
  3232. }
  3233. }
  3234. }
  3235. push_list[x]->client->ps.otherKiller = self->s.number;
  3236. push_list[x]->client->ps.otherKillerTime = level.time + 5000;
  3237. push_list[x]->client->ps.otherKillerDebounceTime = level.time + 100;
  3238. pushPowerMod -= (dirLen*0.7);
  3239. if (pushPowerMod < 16)
  3240. {
  3241. pushPowerMod = 16;
  3242. }
  3243. //fullbody push effect
  3244. push_list[x]->client->pushEffectTime = level.time + 600;
  3245. push_list[x]->client->ps.velocity[0] = pushDir[0]*pushPowerMod;
  3246. push_list[x]->client->ps.velocity[1] = pushDir[1]*pushPowerMod;
  3247. if ((int)push_list[x]->client->ps.velocity[2] == 0)
  3248. { //if not going anywhere vertically, boost them up a bit
  3249. push_list[x]->client->ps.velocity[2] = pushDir[2]*pushPowerMod;
  3250. if (push_list[x]->client->ps.velocity[2] < 128)
  3251. {
  3252. push_list[x]->client->ps.velocity[2] = 128;
  3253. }
  3254. }
  3255. else
  3256. {
  3257. push_list[x]->client->ps.velocity[2] = pushDir[2]*pushPowerMod;
  3258. }
  3259. }
  3260. }
  3261. else if ( push_list[x]->s.eType == ET_MISSILE && push_list[x]->s.pos.trType != TR_STATIONARY && (push_list[x]->s.pos.trType != TR_INTERPOLATE||push_list[x]->s.weapon != WP_THERMAL) )//rolling and stationary thermal detonators are dealt with below
  3262. {
  3263. if ( pull )
  3264. {//deflect rather than reflect?
  3265. }
  3266. else
  3267. {
  3268. G_ReflectMissile( self, push_list[x], forward );
  3269. }
  3270. }
  3271. else if ( !Q_stricmp( "func_static", push_list[x]->classname ) )
  3272. {//force-usable func_static
  3273. if ( !pull && (push_list[x]->spawnflags&1/*F_PUSH*/) )
  3274. {
  3275. GEntity_UseFunc( push_list[x], self, self );
  3276. }
  3277. else if ( pull && (push_list[x]->spawnflags&2/*F_PULL*/) )
  3278. {
  3279. GEntity_UseFunc( push_list[x], self, self );
  3280. }
  3281. }
  3282. else if ( !Q_stricmp( "func_door", push_list[x]->classname ) && (push_list[x]->spawnflags&2) )
  3283. {//push/pull the door
  3284. vec3_t pos1, pos2;
  3285. vec3_t trFrom;
  3286. VectorCopy(self->client->ps.origin, trFrom);
  3287. trFrom[2] += self->client->ps.viewheight;
  3288. AngleVectors( self->client->ps.viewangles, forward, NULL, NULL );
  3289. VectorNormalize( forward );
  3290. VectorMA( trFrom, radius, forward, end );
  3291. trap_Trace( &tr, trFrom, vec3_origin, vec3_origin, end, self->s.number, MASK_SHOT );
  3292. if ( tr.entityNum != push_list[x]->s.number || tr.fraction == 1.0 || tr.allsolid || tr.startsolid )
  3293. {//must be pointing right at it
  3294. continue;
  3295. }
  3296. if ( VectorCompare( vec3_origin, push_list[x]->s.origin ) )
  3297. {//does not have an origin brush, so pos1 & pos2 are relative to world origin, need to calc center
  3298. VectorSubtract( push_list[x]->r.absmax, push_list[x]->r.absmin, size );
  3299. VectorMA( push_list[x]->r.absmin, 0.5, size, center );
  3300. if ( (push_list[x]->spawnflags&1) && push_list[x]->moverState == MOVER_POS1 )
  3301. {//if at pos1 and started open, make sure we get the center where it *started* because we're going to add back in the relative values pos1 and pos2
  3302. VectorSubtract( center, push_list[x]->pos1, center );
  3303. }
  3304. else if ( !(push_list[x]->spawnflags&1) && push_list[x]->moverState == MOVER_POS2 )
  3305. {//if at pos2, make sure we get the center where it *started* because we're going to add back in the relative values pos1 and pos2
  3306. VectorSubtract( center, push_list[x]->pos2, center );
  3307. }
  3308. VectorAdd( center, push_list[x]->pos1, pos1 );
  3309. VectorAdd( center, push_list[x]->pos2, pos2 );
  3310. }
  3311. else
  3312. {//actually has an origin, pos1 and pos2 are absolute
  3313. VectorCopy( push_list[x]->r.currentOrigin, center );
  3314. VectorCopy( push_list[x]->pos1, pos1 );
  3315. VectorCopy( push_list[x]->pos2, pos2 );
  3316. }
  3317. if ( Distance( pos1, trFrom ) < Distance( pos2, trFrom ) )
  3318. {//pos1 is closer
  3319. if ( push_list[x]->moverState == MOVER_POS1 )
  3320. {//at the closest pos
  3321. if ( pull )
  3322. {//trying to pull, but already at closest point, so screw it
  3323. continue;
  3324. }
  3325. }
  3326. else if ( push_list[x]->moverState == MOVER_POS2 )
  3327. {//at farthest pos
  3328. if ( !pull )
  3329. {//trying to push, but already at farthest point, so screw it
  3330. continue;
  3331. }
  3332. }
  3333. }
  3334. else
  3335. {//pos2 is closer
  3336. if ( push_list[x]->moverState == MOVER_POS1 )
  3337. {//at the farthest pos
  3338. if ( !pull )
  3339. {//trying to push, but already at farthest point, so screw it
  3340. continue;
  3341. }
  3342. }
  3343. else if ( push_list[x]->moverState == MOVER_POS2 )
  3344. {//at closest pos
  3345. if ( pull )
  3346. {//trying to pull, but already at closest point, so screw it
  3347. continue;
  3348. }
  3349. }
  3350. }
  3351. GEntity_UseFunc( push_list[x], self, self );
  3352. }
  3353. else if ( Q_stricmp( "func_button", push_list[x]->classname ) == 0 )
  3354. {//pretend you pushed it
  3355. Touch_Button( push_list[x], self, NULL );
  3356. continue;
  3357. }
  3358. }
  3359. }
  3360. //attempt to break any leftover grips
  3361. //if we're still in a current grip that wasn't broken by the push, it will still remain
  3362. self->client->dangerTime = level.time;
  3363. self->client->ps.eFlags &= ~EF_INVULNERABLE;
  3364. self->client->invulnerableTimer = 0;
  3365. if (self->client->ps.fd.forceGripBeingGripped > level.time)
  3366. {
  3367. self->client->ps.fd.forceGripBeingGripped = 0;
  3368. }
  3369. }
  3370. void WP_ForcePowerStop( gentity_t *self, forcePowers_t forcePower )
  3371. {
  3372. int wasActive = self->client->ps.fd.forcePowersActive;
  3373. self->client->ps.fd.forcePowersActive &= ~( 1 << forcePower );
  3374. switch( (int)forcePower )
  3375. {
  3376. case FP_HEAL:
  3377. self->client->ps.fd.forceHealAmount = 0;
  3378. self->client->ps.fd.forceHealTime = 0;
  3379. break;
  3380. case FP_LEVITATION:
  3381. break;
  3382. case FP_SPEED:
  3383. if (wasActive & (1 << FP_SPEED))
  3384. {
  3385. G_MuteSound(self->client->ps.fd.killSoundEntIndex[TRACK_CHANNEL_2-50], CHAN_VOICE);
  3386. }
  3387. break;
  3388. case FP_PUSH:
  3389. break;
  3390. case FP_PULL:
  3391. break;
  3392. case FP_TELEPATHY:
  3393. if (wasActive & (1 << FP_TELEPATHY))
  3394. {
  3395. G_Sound( self, CHAN_AUTO, G_SoundIndex("sound/weapons/force/distractstop.wav") );
  3396. }
  3397. self->client->ps.fd.forceMindtrickTargetIndex = 0;
  3398. self->client->ps.fd.forceMindtrickTargetIndex2 = 0;
  3399. self->client->ps.fd.forceMindtrickTargetIndex3 = 0;
  3400. self->client->ps.fd.forceMindtrickTargetIndex4 = 0;
  3401. break;
  3402. case FP_SEE:
  3403. if (wasActive & (1 << FP_SEE))
  3404. {
  3405. G_MuteSound(self->client->ps.fd.killSoundEntIndex[TRACK_CHANNEL_5-50], CHAN_VOICE);
  3406. }
  3407. break;
  3408. case FP_GRIP:
  3409. self->client->ps.fd.forceGripUseTime = level.time + 3000;
  3410. if (self->client->ps.fd.forcePowerLevel[FP_GRIP] > FORCE_LEVEL_1 &&
  3411. g_entities[self->client->ps.fd.forceGripEntityNum].client &&
  3412. g_entities[self->client->ps.fd.forceGripEntityNum].health > 0 &&
  3413. g_entities[self->client->ps.fd.forceGripEntityNum].inuse &&
  3414. (level.time - g_entities[self->client->ps.fd.forceGripEntityNum].client->ps.fd.forceGripStarted) > 500)
  3415. { //if we had our throat crushed in for more than half a second, gasp for air when we're let go
  3416. if (wasActive & (1 << FP_GRIP))
  3417. {
  3418. G_EntitySound( &g_entities[self->client->ps.fd.forceGripEntityNum], CHAN_VOICE, G_SoundIndex("*gasp.wav") );
  3419. }
  3420. }
  3421. if (g_entities[self->client->ps.fd.forceGripEntityNum].client &&
  3422. g_entities[self->client->ps.fd.forceGripEntityNum].inuse)
  3423. {
  3424. g_entities[self->client->ps.fd.forceGripEntityNum].client->ps.forceGripChangeMovetype = PM_NORMAL;
  3425. }
  3426. if (self->client->ps.forceHandExtend == HANDEXTEND_FORCE_HOLD)
  3427. {
  3428. self->client->ps.forceHandExtendTime = 0;
  3429. }
  3430. self->client->ps.fd.forceGripEntityNum = ENTITYNUM_NONE;
  3431. self->client->ps.powerups[PW_DISINT_4] = 0;
  3432. break;
  3433. case FP_LIGHTNING:
  3434. if ( self->client->ps.fd.forcePowerLevel[FP_LIGHTNING] < FORCE_LEVEL_2 )
  3435. {//don't do it again for 3 seconds, minimum... FIXME: this should be automatic once regeneration is slower (normal)
  3436. self->client->ps.fd.forcePowerDebounce[FP_LIGHTNING] = level.time + 3000;
  3437. }
  3438. else
  3439. {
  3440. self->client->ps.fd.forcePowerDebounce[FP_LIGHTNING] = level.time + 1500;
  3441. }
  3442. if (self->client->ps.forceHandExtend == HANDEXTEND_FORCE_HOLD)
  3443. {
  3444. self->client->ps.forceHandExtendTime = 0; //reset hand position
  3445. }
  3446. self->client->ps.activeForcePass = 0;
  3447. break;
  3448. case FP_RAGE:
  3449. self->client->ps.fd.forceRageRecoveryTime = level.time + 10000;
  3450. if (wasActive & (1 << FP_RAGE))
  3451. {
  3452. G_MuteSound(self->client->ps.fd.killSoundEntIndex[TRACK_CHANNEL_3-50], CHAN_VOICE);
  3453. }
  3454. break;
  3455. case FP_ABSORB:
  3456. if (wasActive & (1 << FP_ABSORB))
  3457. {
  3458. G_MuteSound(self->client->ps.fd.killSoundEntIndex[TRACK_CHANNEL_3-50], CHAN_VOICE);
  3459. }
  3460. break;
  3461. case FP_PROTECT:
  3462. if (wasActive & (1 << FP_PROTECT))
  3463. {
  3464. G_MuteSound(self->client->ps.fd.killSoundEntIndex[TRACK_CHANNEL_3-50], CHAN_VOICE);
  3465. }
  3466. break;
  3467. case FP_DRAIN:
  3468. if ( self->client->ps.fd.forcePowerLevel[FP_DRAIN] < FORCE_LEVEL_2 )
  3469. {//don't do it again for 3 seconds, minimum...
  3470. self->client->ps.fd.forcePowerDebounce[FP_DRAIN] = level.time + 3000;
  3471. }
  3472. else
  3473. {
  3474. self->client->ps.fd.forcePowerDebounce[FP_DRAIN] = level.time + 1500;
  3475. }
  3476. if (self->client->ps.forceHandExtend == HANDEXTEND_FORCE_HOLD)
  3477. {
  3478. self->client->ps.forceHandExtendTime = 0; //reset hand position
  3479. }
  3480. self->client->ps.activeForcePass = 0;
  3481. default:
  3482. break;
  3483. }
  3484. }
  3485. void DoGripAction(gentity_t *self, forcePowers_t forcePower)
  3486. {
  3487. gentity_t *gripEnt;
  3488. int gripLevel = 0;
  3489. trace_t tr;
  3490. vec3_t a;
  3491. vec3_t fwd, fwd_o, start_o, nvel;
  3492. self->client->dangerTime = level.time;
  3493. self->client->ps.eFlags &= ~EF_INVULNERABLE;
  3494. self->client->invulnerableTimer = 0;
  3495. gripEnt = &g_entities[self->client->ps.fd.forceGripEntityNum];
  3496. if (!gripEnt || !gripEnt->client || !gripEnt->inuse || gripEnt->health < 1 || !ForcePowerUsableOn(self, gripEnt, FP_GRIP))
  3497. {
  3498. WP_ForcePowerStop(self, forcePower);
  3499. self->client->ps.fd.forceGripEntityNum = ENTITYNUM_NONE;
  3500. if (gripEnt && gripEnt->client && gripEnt->inuse)
  3501. {
  3502. gripEnt->client->ps.forceGripChangeMovetype = PM_NORMAL;
  3503. }
  3504. return;
  3505. }
  3506. VectorSubtract(gripEnt->client->ps.origin, self->client->ps.origin, a);
  3507. trap_Trace(&tr, self->client->ps.origin, NULL, NULL, gripEnt->client->ps.origin, self->s.number, MASK_PLAYERSOLID);
  3508. gripLevel = WP_AbsorbConversion(gripEnt, gripEnt->client->ps.fd.forcePowerLevel[FP_ABSORB], self, FP_GRIP, self->client->ps.fd.forcePowerLevel[FP_GRIP], forcePowerNeeded[self->client->ps.fd.forcePowerLevel[FP_GRIP]][FP_GRIP]);
  3509. if (gripLevel == -1)
  3510. {
  3511. gripLevel = self->client->ps.fd.forcePowerLevel[FP_GRIP];
  3512. }
  3513. if (!gripLevel)
  3514. {
  3515. WP_ForcePowerStop(self, forcePower);
  3516. return;
  3517. }
  3518. if (VectorLength(a) > MAX_GRIP_DISTANCE)
  3519. {
  3520. WP_ForcePowerStop(self, forcePower);
  3521. return;
  3522. }
  3523. if ( !InFront( gripEnt->client->ps.origin, self->client->ps.origin, self->client->ps.viewangles, 0.9f ) &&
  3524. gripLevel < FORCE_LEVEL_3)
  3525. {
  3526. WP_ForcePowerStop(self, forcePower);
  3527. return;
  3528. }
  3529. if (tr.fraction != 1.0f &&
  3530. tr.entityNum != gripEnt->s.number /*&&
  3531. gripLevel < FORCE_LEVEL_3*/)
  3532. {
  3533. WP_ForcePowerStop(self, forcePower);
  3534. return;
  3535. }
  3536. if (self->client->ps.fd.forcePowerDebounce[FP_GRIP] < level.time)
  3537. { //2 damage per second while choking, resulting in 10 damage total (not including The Squeeze<tm>)
  3538. self->client->ps.fd.forcePowerDebounce[FP_GRIP] = level.time + 1000;
  3539. G_Damage(gripEnt, self, self, NULL, NULL, 2, DAMAGE_NO_ARMOR, MOD_FORCE_DARK);
  3540. }
  3541. Jetpack_Off(gripEnt); //make sure the guy being gripped has his jetpack off.
  3542. if (gripLevel == FORCE_LEVEL_1)
  3543. {
  3544. gripEnt->client->ps.fd.forceGripBeingGripped = level.time + 1000;
  3545. if ((level.time - gripEnt->client->ps.fd.forceGripStarted) > 5000)
  3546. {
  3547. WP_ForcePowerStop(self, forcePower);
  3548. }
  3549. return;
  3550. }
  3551. if (gripLevel == FORCE_LEVEL_2)
  3552. {
  3553. gripEnt->client->ps.fd.forceGripBeingGripped = level.time + 1000;
  3554. if (gripEnt->client->ps.forceGripMoveInterval < level.time)
  3555. {
  3556. gripEnt->client->ps.velocity[2] = 30;
  3557. gripEnt->client->ps.forceGripMoveInterval = level.time + 300; //only update velocity every 300ms, so as to avoid heavy bandwidth usage
  3558. }
  3559. gripEnt->client->ps.otherKiller = self->s.number;
  3560. gripEnt->client->ps.otherKillerTime = level.time + 5000;
  3561. gripEnt->client->ps.otherKillerDebounceTime = level.time + 100;
  3562. gripEnt->client->ps.forceGripChangeMovetype = PM_FLOAT;
  3563. if ((level.time - gripEnt->client->ps.fd.forceGripStarted) > 3000 && !self->client->ps.fd.forceGripDamageDebounceTime)
  3564. { //if we managed to lift him into the air for 2 seconds, give him a crack
  3565. self->client->ps.fd.forceGripDamageDebounceTime = 1;
  3566. G_Damage(gripEnt, self, self, NULL, NULL, 20, DAMAGE_NO_ARMOR, MOD_FORCE_DARK);
  3567. //Must play custom sounds on the actual entity. Don't use G_Sound (it creates a temp entity for the sound)
  3568. G_EntitySound( gripEnt, CHAN_VOICE, G_SoundIndex(va( "*choke%d.wav", Q_irand( 1, 3 ) )) );
  3569. gripEnt->client->ps.forceHandExtend = HANDEXTEND_CHOKE;
  3570. gripEnt->client->ps.forceHandExtendTime = level.time + 2000;
  3571. if (gripEnt->client->ps.fd.forcePowersActive & (1 << FP_GRIP))
  3572. { //choking, so don't let him keep gripping himself
  3573. WP_ForcePowerStop(gripEnt, FP_GRIP);
  3574. }
  3575. }
  3576. else if ((level.time - gripEnt->client->ps.fd.forceGripStarted) > 4000)
  3577. {
  3578. WP_ForcePowerStop(self, forcePower);
  3579. }
  3580. return;
  3581. }
  3582. if (gripLevel == FORCE_LEVEL_3)
  3583. {
  3584. gripEnt->client->ps.fd.forceGripBeingGripped = level.time + 1000;
  3585. gripEnt->client->ps.otherKiller = self->s.number;
  3586. gripEnt->client->ps.otherKillerTime = level.time + 5000;
  3587. gripEnt->client->ps.otherKillerDebounceTime = level.time + 100;
  3588. gripEnt->client->ps.forceGripChangeMovetype = PM_FLOAT;
  3589. if (gripEnt->client->ps.forceGripMoveInterval < level.time)
  3590. {
  3591. float nvLen = 0;
  3592. VectorCopy(gripEnt->client->ps.origin, start_o);
  3593. AngleVectors(self->client->ps.viewangles, fwd, NULL, NULL);
  3594. fwd_o[0] = self->client->ps.origin[0] + fwd[0]*128;
  3595. fwd_o[1] = self->client->ps.origin[1] + fwd[1]*128;
  3596. fwd_o[2] = self->client->ps.origin[2] + fwd[2]*128;
  3597. fwd_o[2] += 16;
  3598. VectorSubtract(fwd_o, start_o, nvel);
  3599. nvLen = VectorLength(nvel);
  3600. if (nvLen < 16)
  3601. { //within x units of desired spot
  3602. VectorNormalize(nvel);
  3603. gripEnt->client->ps.velocity[0] = nvel[0]*8;
  3604. gripEnt->client->ps.velocity[1] = nvel[1]*8;
  3605. gripEnt->client->ps.velocity[2] = nvel[2]*8;
  3606. }
  3607. else if (nvLen < 64)
  3608. {
  3609. VectorNormalize(nvel);
  3610. gripEnt->client->ps.velocity[0] = nvel[0]*128;
  3611. gripEnt->client->ps.velocity[1] = nvel[1]*128;
  3612. gripEnt->client->ps.velocity[2] = nvel[2]*128;
  3613. }
  3614. else if (nvLen < 128)
  3615. {
  3616. VectorNormalize(nvel);
  3617. gripEnt->client->ps.velocity[0] = nvel[0]*256;
  3618. gripEnt->client->ps.velocity[1] = nvel[1]*256;
  3619. gripEnt->client->ps.velocity[2] = nvel[2]*256;
  3620. }
  3621. else if (nvLen < 200)
  3622. {
  3623. VectorNormalize(nvel);
  3624. gripEnt->client->ps.velocity[0] = nvel[0]*512;
  3625. gripEnt->client->ps.velocity[1] = nvel[1]*512;
  3626. gripEnt->client->ps.velocity[2] = nvel[2]*512;
  3627. }
  3628. else
  3629. {
  3630. VectorNormalize(nvel);
  3631. gripEnt->client->ps.velocity[0] = nvel[0]*700;
  3632. gripEnt->client->ps.velocity[1] = nvel[1]*700;
  3633. gripEnt->client->ps.velocity[2] = nvel[2]*700;
  3634. }
  3635. gripEnt->client->ps.forceGripMoveInterval = level.time + 300; //only update velocity every 300ms, so as to avoid heavy bandwidth usage
  3636. }
  3637. if ((level.time - gripEnt->client->ps.fd.forceGripStarted) > 3000 && !self->client->ps.fd.forceGripDamageDebounceTime)
  3638. { //if we managed to lift him into the air for 2 seconds, give him a crack
  3639. self->client->ps.fd.forceGripDamageDebounceTime = 1;
  3640. G_Damage(gripEnt, self, self, NULL, NULL, 40, DAMAGE_NO_ARMOR, MOD_FORCE_DARK);
  3641. //Must play custom sounds on the actual entity. Don't use G_Sound (it creates a temp entity for the sound)
  3642. G_EntitySound( gripEnt, CHAN_VOICE, G_SoundIndex(va( "*choke%d.wav", Q_irand( 1, 3 ) )) );
  3643. gripEnt->client->ps.forceHandExtend = HANDEXTEND_CHOKE;
  3644. gripEnt->client->ps.forceHandExtendTime = level.time + 2000;
  3645. if (gripEnt->client->ps.fd.forcePowersActive & (1 << FP_GRIP))
  3646. { //choking, so don't let him keep gripping himself
  3647. WP_ForcePowerStop(gripEnt, FP_GRIP);
  3648. }
  3649. }
  3650. else if ((level.time - gripEnt->client->ps.fd.forceGripStarted) > 4000)
  3651. {
  3652. WP_ForcePowerStop(self, forcePower);
  3653. }
  3654. return;
  3655. }
  3656. }
  3657. qboolean G_IsMindTricked(forcedata_t *fd, int client)
  3658. {
  3659. int checkIn;
  3660. int trickIndex1, trickIndex2, trickIndex3, trickIndex4;
  3661. int sub = 0;
  3662. if (!fd)
  3663. {
  3664. return qfalse;
  3665. }
  3666. trickIndex1 = fd->forceMindtrickTargetIndex;
  3667. trickIndex2 = fd->forceMindtrickTargetIndex2;
  3668. trickIndex3 = fd->forceMindtrickTargetIndex3;
  3669. trickIndex4 = fd->forceMindtrickTargetIndex4;
  3670. if (client > 47)
  3671. {
  3672. checkIn = trickIndex4;
  3673. sub = 48;
  3674. }
  3675. else if (client > 31)
  3676. {
  3677. checkIn = trickIndex3;
  3678. sub = 32;
  3679. }
  3680. else if (client > 15)
  3681. {
  3682. checkIn = trickIndex2;
  3683. sub = 16;
  3684. }
  3685. else
  3686. {
  3687. checkIn = trickIndex1;
  3688. }
  3689. if (checkIn & (1 << (client-sub)))
  3690. {
  3691. return qtrue;
  3692. }
  3693. return qfalse;
  3694. }
  3695. static void RemoveTrickedEnt(forcedata_t *fd, int client)
  3696. {
  3697. if (!fd)
  3698. {
  3699. return;
  3700. }
  3701. if (client > 47)
  3702. {
  3703. fd->forceMindtrickTargetIndex4 &= ~(1 << (client-48));
  3704. }
  3705. else if (client > 31)
  3706. {
  3707. fd->forceMindtrickTargetIndex3 &= ~(1 << (client-32));
  3708. }
  3709. else if (client > 15)
  3710. {
  3711. fd->forceMindtrickTargetIndex2 &= ~(1 << (client-16));
  3712. }
  3713. else
  3714. {
  3715. fd->forceMindtrickTargetIndex &= ~(1 << client);
  3716. }
  3717. }
  3718. extern int g_LastFrameTime;
  3719. extern int g_TimeSinceLastFrame;
  3720. static void WP_UpdateMindtrickEnts(gentity_t *self)
  3721. {
  3722. int i = 0;
  3723. while (i < MAX_CLIENTS)
  3724. {
  3725. if (G_IsMindTricked(&self->client->ps.fd, i))
  3726. {
  3727. gentity_t *ent = &g_entities[i];
  3728. if ( !ent || !ent->client || !ent->inuse || ent->health < 1 ||
  3729. (ent->client->ps.fd.forcePowersActive & (1 << FP_SEE)) )
  3730. {
  3731. RemoveTrickedEnt(&self->client->ps.fd, i);
  3732. }
  3733. else if ((level.time - self->client->dangerTime) < g_TimeSinceLastFrame*4)
  3734. { //Untrick this entity if the tricker (self) fires while in his fov
  3735. if (trap_InPVS(ent->client->ps.origin, self->client->ps.origin) &&
  3736. OrgVisible(ent->client->ps.origin, self->client->ps.origin, ent->s.number))
  3737. {
  3738. RemoveTrickedEnt(&self->client->ps.fd, i);
  3739. }
  3740. }
  3741. else if (BG_HasYsalamiri(level.gametype, &ent->client->ps))
  3742. {
  3743. RemoveTrickedEnt(&self->client->ps.fd, i);
  3744. }
  3745. }
  3746. i++;
  3747. }
  3748. if (!self->client->ps.fd.forceMindtrickTargetIndex &&
  3749. !self->client->ps.fd.forceMindtrickTargetIndex2 &&
  3750. !self->client->ps.fd.forceMindtrickTargetIndex3 &&
  3751. !self->client->ps.fd.forceMindtrickTargetIndex4)
  3752. { //everyone who we had tricked is no longer tricked, so stop the power
  3753. WP_ForcePowerStop(self, FP_TELEPATHY);
  3754. }
  3755. else if (self->client->ps.powerups[PW_REDFLAG] ||
  3756. self->client->ps.powerups[PW_BLUEFLAG])
  3757. {
  3758. WP_ForcePowerStop(self, FP_TELEPATHY);
  3759. }
  3760. }
  3761. //JAC: sets the time between lightning/drain hit shots on the server so that we can alter the sv_fps without issues.
  3762. #define FORCE_DEBOUNCE_TIME 50 // sv_fps 20 = 50msec frametime, basejka balance/timing
  3763. static void WP_ForcePowerRun( gentity_t *self, forcePowers_t forcePower, usercmd_t *cmd )
  3764. {
  3765. // extern usercmd_t ucmd;
  3766. switch( (int)forcePower )
  3767. {
  3768. case FP_HEAL:
  3769. if (self->client->ps.fd.forcePowerLevel[FP_HEAL] == FORCE_LEVEL_1)
  3770. {
  3771. if (self->client->ps.velocity[0] || self->client->ps.velocity[1] || self->client->ps.velocity[2])
  3772. {
  3773. WP_ForcePowerStop( self, forcePower );
  3774. break;
  3775. }
  3776. }
  3777. if (self->health < 1 || self->client->ps.stats[STAT_HEALTH] < 1)
  3778. {
  3779. WP_ForcePowerStop( self, forcePower );
  3780. break;
  3781. }
  3782. if (self->client->ps.fd.forceHealTime > level.time)
  3783. {
  3784. break;
  3785. }
  3786. if ( self->health > self->client->ps.stats[STAT_MAX_HEALTH])
  3787. { //rww - we might start out over max_health and we don't want force heal taking us down to 100 or whatever max_health is
  3788. WP_ForcePowerStop( self, forcePower );
  3789. break;
  3790. }
  3791. self->client->ps.fd.forceHealTime = level.time + 1000;
  3792. self->health++;
  3793. self->client->ps.fd.forceHealAmount++;
  3794. if ( self->health > self->client->ps.stats[STAT_MAX_HEALTH]) // Past max health
  3795. {
  3796. self->health = self->client->ps.stats[STAT_MAX_HEALTH];
  3797. WP_ForcePowerStop( self, forcePower );
  3798. }
  3799. if ( (self->client->ps.fd.forcePowerLevel[FP_HEAL] == FORCE_LEVEL_1 && self->client->ps.fd.forceHealAmount >= 25) ||
  3800. (self->client->ps.fd.forcePowerLevel[FP_HEAL] == FORCE_LEVEL_2 && self->client->ps.fd.forceHealAmount >= 33))
  3801. {
  3802. WP_ForcePowerStop( self, forcePower );
  3803. }
  3804. break;
  3805. case FP_SPEED:
  3806. //This is handled in PM_WalkMove and PM_StepSlideMove
  3807. if ( self->client->holdingObjectiveItem >= MAX_CLIENTS
  3808. && self->client->holdingObjectiveItem < ENTITYNUM_WORLD )
  3809. {
  3810. if ( g_entities[self->client->holdingObjectiveItem].genericValue15 )
  3811. {//disables force powers
  3812. WP_ForcePowerStop( self, forcePower );
  3813. }
  3814. }
  3815. /*
  3816. if ( self->client->ps.powerups[PW_REDFLAG]
  3817. || self->client->ps.powerups[PW_BLUEFLAG]
  3818. || self->client->ps.powerups[PW_NEUTRALFLAG] )
  3819. {//no force speed when carrying flag
  3820. WP_ForcePowerStop( self, forcePower );
  3821. }
  3822. */
  3823. break;
  3824. case FP_GRIP:
  3825. if (self->client->ps.forceHandExtend != HANDEXTEND_FORCE_HOLD)
  3826. {
  3827. WP_ForcePowerStop(self, FP_GRIP);
  3828. break;
  3829. }
  3830. if (self->client->ps.fd.forcePowerDebounce[FP_PULL] < level.time)
  3831. { //This is sort of not ideal. Using the debounce value reserved for pull for this because pull doesn't need it.
  3832. BG_ForcePowerDrain( &self->client->ps, forcePower, 1 );
  3833. self->client->ps.fd.forcePowerDebounce[FP_PULL] = level.time + 100;
  3834. }
  3835. if (self->client->ps.fd.forcePower < 1)
  3836. {
  3837. WP_ForcePowerStop(self, FP_GRIP);
  3838. break;
  3839. }
  3840. DoGripAction(self, forcePower);
  3841. break;
  3842. case FP_LEVITATION:
  3843. if ( self->client->ps.groundEntityNum != ENTITYNUM_NONE && !self->client->ps.fd.forceJumpZStart )
  3844. {//done with jump
  3845. WP_ForcePowerStop( self, forcePower );
  3846. }
  3847. break;
  3848. case FP_RAGE:
  3849. if (self->health < 1)
  3850. {
  3851. WP_ForcePowerStop(self, forcePower);
  3852. break;
  3853. }
  3854. if (self->client->ps.forceRageDrainTime < level.time)
  3855. {
  3856. int addTime = 400;
  3857. self->health -= 2;
  3858. if (self->client->ps.fd.forcePowerLevel[FP_RAGE] == FORCE_LEVEL_1)
  3859. {
  3860. addTime = 150;
  3861. }
  3862. else if (self->client->ps.fd.forcePowerLevel[FP_RAGE] == FORCE_LEVEL_2)
  3863. {
  3864. addTime = 300;
  3865. }
  3866. else if (self->client->ps.fd.forcePowerLevel[FP_RAGE] == FORCE_LEVEL_3)
  3867. {
  3868. addTime = 450;
  3869. }
  3870. self->client->ps.forceRageDrainTime = level.time + addTime;
  3871. }
  3872. if (self->health < 1)
  3873. {
  3874. self->health = 1;
  3875. WP_ForcePowerStop(self, forcePower);
  3876. }
  3877. self->client->ps.stats[STAT_HEALTH] = self->health;
  3878. break;
  3879. case FP_DRAIN:
  3880. if (self->client->ps.forceHandExtend != HANDEXTEND_FORCE_HOLD)
  3881. {
  3882. WP_ForcePowerStop(self, forcePower);
  3883. break;
  3884. }
  3885. if ( self->client->ps.fd.forcePowerLevel[FP_DRAIN] > FORCE_LEVEL_1 )
  3886. {//higher than level 1
  3887. if ( (cmd->buttons & BUTTON_FORCE_DRAIN) || ((cmd->buttons & BUTTON_FORCEPOWER) && self->client->ps.fd.forcePowerSelected == FP_DRAIN) )
  3888. {//holding it keeps it going
  3889. self->client->ps.fd.forcePowerDuration[FP_DRAIN] = level.time + 500;
  3890. }
  3891. }
  3892. // OVERRIDEFIXME
  3893. if ( !WP_ForcePowerAvailable( self, forcePower, 0 ) || self->client->ps.fd.forcePowerDuration[FP_DRAIN] < level.time ||
  3894. self->client->ps.fd.forcePower < 25)
  3895. {
  3896. WP_ForcePowerStop( self, forcePower );
  3897. }
  3898. //JAC: consistent drain regardless of sv_fps
  3899. else
  3900. {
  3901. while ( self->client->force.drainDebounce < level.time )
  3902. {
  3903. ForceShootDrain( self );
  3904. self->client->force.drainDebounce += FORCE_DEBOUNCE_TIME;
  3905. }
  3906. }
  3907. break;
  3908. case FP_LIGHTNING:
  3909. if (self->client->ps.forceHandExtend != HANDEXTEND_FORCE_HOLD)
  3910. { //Animation for hand extend doesn't end with hand out, so we have to limit lightning intervals by animation intervals (once hand starts to go in in animation, lightning should stop)
  3911. WP_ForcePowerStop(self, forcePower);
  3912. break;
  3913. }
  3914. if ( self->client->ps.fd.forcePowerLevel[FP_LIGHTNING] > FORCE_LEVEL_1 )
  3915. {//higher than level 1
  3916. if ( (cmd->buttons & BUTTON_FORCE_LIGHTNING) || ((cmd->buttons & BUTTON_FORCEPOWER) && self->client->ps.fd.forcePowerSelected == FP_LIGHTNING) )
  3917. {//holding it keeps it going
  3918. self->client->ps.fd.forcePowerDuration[FP_LIGHTNING] = level.time + 500;
  3919. }
  3920. }
  3921. // OVERRIDEFIXME
  3922. if ( !WP_ForcePowerAvailable( self, forcePower, 0 ) || self->client->ps.fd.forcePowerDuration[FP_LIGHTNING] < level.time ||
  3923. self->client->ps.fd.forcePower < 25)
  3924. {
  3925. WP_ForcePowerStop( self, forcePower );
  3926. }
  3927. //JAC: consistent lightning regardless of sv_fps
  3928. else
  3929. {
  3930. while ( self->client->force.lightningDebounce < level.time )
  3931. {
  3932. ForceShootLightning( self );
  3933. BG_ForcePowerDrain( &self->client->ps, forcePower, 0 );
  3934. self->client->force.lightningDebounce += FORCE_DEBOUNCE_TIME;
  3935. }
  3936. }
  3937. break;
  3938. case FP_TELEPATHY:
  3939. if ( self->client->holdingObjectiveItem >= MAX_CLIENTS
  3940. && self->client->holdingObjectiveItem < ENTITYNUM_WORLD
  3941. && g_entities[self->client->holdingObjectiveItem].genericValue15 )
  3942. { //if force hindered can't mindtrick whilst carrying a siege item
  3943. WP_ForcePowerStop( self, FP_TELEPATHY );
  3944. }
  3945. else
  3946. {
  3947. WP_UpdateMindtrickEnts(self);
  3948. }
  3949. break;
  3950. case FP_SABER_OFFENSE:
  3951. break;
  3952. case FP_SABER_DEFENSE:
  3953. break;
  3954. case FP_SABERTHROW:
  3955. break;
  3956. case FP_PROTECT:
  3957. if (self->client->ps.fd.forcePowerDebounce[forcePower] < level.time)
  3958. {
  3959. BG_ForcePowerDrain( &self->client->ps, forcePower, 1 );
  3960. if (self->client->ps.fd.forcePower < 1)
  3961. {
  3962. WP_ForcePowerStop(self, forcePower);
  3963. }
  3964. self->client->ps.fd.forcePowerDebounce[forcePower] = level.time + 300;
  3965. }
  3966. break;
  3967. case FP_ABSORB:
  3968. if (self->client->ps.fd.forcePowerDebounce[forcePower] < level.time)
  3969. {
  3970. BG_ForcePowerDrain( &self->client->ps, forcePower, 1 );
  3971. if (self->client->ps.fd.forcePower < 1)
  3972. {
  3973. WP_ForcePowerStop(self, forcePower);
  3974. }
  3975. self->client->ps.fd.forcePowerDebounce[forcePower] = level.time + 600;
  3976. }
  3977. break;
  3978. default:
  3979. break;
  3980. }
  3981. }
  3982. int WP_DoSpecificPower( gentity_t *self, usercmd_t *ucmd, forcePowers_t forcepower)
  3983. {
  3984. int powerSucceeded;
  3985. powerSucceeded = 1;
  3986. // OVERRIDEFIXME
  3987. if ( !WP_ForcePowerAvailable( self, forcepower, 0 ) )
  3988. {
  3989. return 0;
  3990. }
  3991. switch(forcepower)
  3992. {
  3993. case FP_HEAL:
  3994. powerSucceeded = 0; //always 0 for nonhold powers
  3995. if (self->client->ps.fd.forceButtonNeedRelease)
  3996. { //need to release before we can use nonhold powers again
  3997. break;
  3998. }
  3999. ForceHeal(self);
  4000. self->client->ps.fd.forceButtonNeedRelease = 1;
  4001. break;
  4002. case FP_LEVITATION:
  4003. //if leave the ground by some other means, cancel the force jump so we don't suddenly jump when we land.
  4004. if ( self->client->ps.groundEntityNum == ENTITYNUM_NONE )
  4005. {
  4006. self->client->ps.fd.forceJumpCharge = 0;
  4007. G_MuteSound( self->client->ps.fd.killSoundEntIndex[TRACK_CHANNEL_1-50], CHAN_VOICE );
  4008. //This only happens if the groundEntityNum == ENTITYNUM_NONE when the button is actually released
  4009. }
  4010. else
  4011. {//still on ground, so jump
  4012. ForceJump( self, ucmd );
  4013. }
  4014. break;
  4015. case FP_SPEED:
  4016. powerSucceeded = 0; //always 0 for nonhold powers
  4017. if (self->client->ps.fd.forceButtonNeedRelease)
  4018. { //need to release before we can use nonhold powers again
  4019. break;
  4020. }
  4021. ForceSpeed(self, 0);
  4022. self->client->ps.fd.forceButtonNeedRelease = 1;
  4023. break;
  4024. case FP_GRIP:
  4025. if (self->client->ps.fd.forceGripEntityNum == ENTITYNUM_NONE)
  4026. {
  4027. ForceGrip( self );
  4028. }
  4029. if (self->client->ps.fd.forceGripEntityNum != ENTITYNUM_NONE)
  4030. {
  4031. if (!(self->client->ps.fd.forcePowersActive & (1 << FP_GRIP)))
  4032. {
  4033. WP_ForcePowerStart( self, FP_GRIP, 0 );
  4034. BG_ForcePowerDrain( &self->client->ps, FP_GRIP, GRIP_DRAIN_AMOUNT );
  4035. }
  4036. }
  4037. else
  4038. {
  4039. powerSucceeded = 0;
  4040. }
  4041. break;
  4042. case FP_LIGHTNING:
  4043. ForceLightning(self);
  4044. break;
  4045. case FP_PUSH:
  4046. powerSucceeded = 0; //always 0 for nonhold powers
  4047. if (self->client->ps.fd.forceButtonNeedRelease && !(self->r.svFlags & SVF_BOT))
  4048. { //need to release before we can use nonhold powers again
  4049. break;
  4050. }
  4051. ForceThrow(self, qfalse);
  4052. self->client->ps.fd.forceButtonNeedRelease = 1;
  4053. break;
  4054. case FP_PULL:
  4055. powerSucceeded = 0; //always 0 for nonhold powers
  4056. if (self->client->ps.fd.forceButtonNeedRelease)
  4057. { //need to release before we can use nonhold powers again
  4058. break;
  4059. }
  4060. ForceThrow(self, qtrue);
  4061. self->client->ps.fd.forceButtonNeedRelease = 1;
  4062. break;
  4063. case FP_TELEPATHY:
  4064. powerSucceeded = 0; //always 0 for nonhold powers
  4065. if (self->client->ps.fd.forceButtonNeedRelease)
  4066. { //need to release before we can use nonhold powers again
  4067. break;
  4068. }
  4069. ForceTelepathy(self);
  4070. self->client->ps.fd.forceButtonNeedRelease = 1;
  4071. break;
  4072. case FP_RAGE:
  4073. powerSucceeded = 0; //always 0 for nonhold powers
  4074. if (self->client->ps.fd.forceButtonNeedRelease)
  4075. { //need to release before we can use nonhold powers again
  4076. break;
  4077. }
  4078. ForceRage(self);
  4079. self->client->ps.fd.forceButtonNeedRelease = 1;
  4080. break;
  4081. case FP_PROTECT:
  4082. powerSucceeded = 0; //always 0 for nonhold powers
  4083. if (self->client->ps.fd.forceButtonNeedRelease)
  4084. { //need to release before we can use nonhold powers again
  4085. break;
  4086. }
  4087. ForceProtect(self);
  4088. self->client->ps.fd.forceButtonNeedRelease = 1;
  4089. break;
  4090. case FP_ABSORB:
  4091. powerSucceeded = 0; //always 0 for nonhold powers
  4092. if (self->client->ps.fd.forceButtonNeedRelease)
  4093. { //need to release before we can use nonhold powers again
  4094. break;
  4095. }
  4096. ForceAbsorb(self);
  4097. self->client->ps.fd.forceButtonNeedRelease = 1;
  4098. break;
  4099. case FP_TEAM_HEAL:
  4100. powerSucceeded = 0; //always 0 for nonhold powers
  4101. if (self->client->ps.fd.forceButtonNeedRelease)
  4102. { //need to release before we can use nonhold powers again
  4103. break;
  4104. }
  4105. ForceTeamHeal(self);
  4106. self->client->ps.fd.forceButtonNeedRelease = 1;
  4107. break;
  4108. case FP_TEAM_FORCE:
  4109. powerSucceeded = 0; //always 0 for nonhold powers
  4110. if (self->client->ps.fd.forceButtonNeedRelease)
  4111. { //need to release before we can use nonhold powers again
  4112. break;
  4113. }
  4114. ForceTeamForceReplenish(self);
  4115. self->client->ps.fd.forceButtonNeedRelease = 1;
  4116. break;
  4117. case FP_DRAIN:
  4118. ForceDrain(self);
  4119. break;
  4120. case FP_SEE:
  4121. powerSucceeded = 0; //always 0 for nonhold powers
  4122. if (self->client->ps.fd.forceButtonNeedRelease)
  4123. { //need to release before we can use nonhold powers again
  4124. break;
  4125. }
  4126. ForceSeeing(self);
  4127. self->client->ps.fd.forceButtonNeedRelease = 1;
  4128. break;
  4129. case FP_SABER_OFFENSE:
  4130. break;
  4131. case FP_SABER_DEFENSE:
  4132. break;
  4133. case FP_SABERTHROW:
  4134. break;
  4135. default:
  4136. break;
  4137. }
  4138. return powerSucceeded;
  4139. }
  4140. void FindGenericEnemyIndex(gentity_t *self)
  4141. { //Find another client that would be considered a threat.
  4142. int i = 0;
  4143. float tlen;
  4144. gentity_t *ent;
  4145. gentity_t *besten = NULL;
  4146. float blen = 99999999.9f;
  4147. vec3_t a;
  4148. while (i < MAX_CLIENTS)
  4149. {
  4150. ent = &g_entities[i];
  4151. if (ent && ent->client && ent->s.number != self->s.number && ent->health > 0 && !OnSameTeam(self, ent) && ent->client->ps.pm_type != PM_INTERMISSION && ent->client->ps.pm_type != PM_SPECTATOR)
  4152. {
  4153. VectorSubtract(ent->client->ps.origin, self->client->ps.origin, a);
  4154. tlen = VectorLength(a);
  4155. if (tlen < blen &&
  4156. InFront(ent->client->ps.origin, self->client->ps.origin, self->client->ps.viewangles, 0.8f ) &&
  4157. OrgVisible(self->client->ps.origin, ent->client->ps.origin, self->s.number))
  4158. {
  4159. blen = tlen;
  4160. besten = ent;
  4161. }
  4162. }
  4163. i++;
  4164. }
  4165. if (!besten)
  4166. {
  4167. return;
  4168. }
  4169. self->client->ps.genericEnemyIndex = besten->s.number;
  4170. }
  4171. void SeekerDroneUpdate(gentity_t *self)
  4172. {
  4173. vec3_t org, elevated, dir, a, endir;
  4174. gentity_t *en;
  4175. float angle;
  4176. float prefig = 0;
  4177. trace_t tr;
  4178. if (!(self->client->ps.eFlags & EF_SEEKERDRONE))
  4179. {
  4180. self->client->ps.genericEnemyIndex = -1;
  4181. return;
  4182. }
  4183. if (self->health < 1)
  4184. {
  4185. VectorCopy(self->client->ps.origin, elevated);
  4186. elevated[2] += 40;
  4187. angle = ((level.time / 12) & 255) * (M_PI * 2) / 255; //magical numbers make magic happen
  4188. dir[0] = cos(angle) * 20;
  4189. dir[1] = sin(angle) * 20;
  4190. dir[2] = cos(angle) * 5;
  4191. VectorAdd(elevated, dir, org);
  4192. a[ROLL] = 0;
  4193. a[YAW] = 0;
  4194. a[PITCH] = 1;
  4195. G_PlayEffect(EFFECT_SPARK_EXPLOSION, org, a);
  4196. self->client->ps.eFlags &= ~EF_SEEKERDRONE;
  4197. self->client->ps.genericEnemyIndex = -1;
  4198. return;
  4199. }
  4200. if (self->client->ps.droneExistTime >= level.time &&
  4201. self->client->ps.droneExistTime < (level.time+5000))
  4202. {
  4203. self->client->ps.genericEnemyIndex = 1024+self->client->ps.droneExistTime;
  4204. if (self->client->ps.droneFireTime < level.time)
  4205. {
  4206. G_Sound( self, CHAN_BODY, G_SoundIndex("sound/weapons/laser_trap/warning.wav") );
  4207. self->client->ps.droneFireTime = level.time + 100;
  4208. }
  4209. return;
  4210. }
  4211. else if (self->client->ps.droneExistTime < level.time)
  4212. {
  4213. VectorCopy(self->client->ps.origin, elevated);
  4214. elevated[2] += 40;
  4215. prefig = (self->client->ps.droneExistTime-level.time)/80;
  4216. if (prefig > 55)
  4217. {
  4218. prefig = 55;
  4219. }
  4220. else if (prefig < 1)
  4221. {
  4222. prefig = 1;
  4223. }
  4224. elevated[2] -= 55-prefig;
  4225. angle = ((level.time / 12) & 255) * (M_PI * 2) / 255; //magical numbers make magic happen
  4226. dir[0] = cos(angle) * 20;
  4227. dir[1] = sin(angle) * 20;
  4228. dir[2] = cos(angle) * 5;
  4229. VectorAdd(elevated, dir, org);
  4230. a[ROLL] = 0;
  4231. a[YAW] = 0;
  4232. a[PITCH] = 1;
  4233. G_PlayEffect(EFFECT_SPARK_EXPLOSION, org, a);
  4234. self->client->ps.eFlags &= ~EF_SEEKERDRONE;
  4235. self->client->ps.genericEnemyIndex = -1;
  4236. return;
  4237. }
  4238. if (self->client->ps.genericEnemyIndex == -1)
  4239. {
  4240. self->client->ps.genericEnemyIndex = ENTITYNUM_NONE;
  4241. }
  4242. if (self->client->ps.genericEnemyIndex != ENTITYNUM_NONE && self->client->ps.genericEnemyIndex != -1)
  4243. {
  4244. en = &g_entities[self->client->ps.genericEnemyIndex];
  4245. if (!en || !en->client)
  4246. {
  4247. self->client->ps.genericEnemyIndex = ENTITYNUM_NONE;
  4248. }
  4249. else if (en->s.number == self->s.number)
  4250. {
  4251. self->client->ps.genericEnemyIndex = ENTITYNUM_NONE;
  4252. }
  4253. else if (en->health < 1)
  4254. {
  4255. self->client->ps.genericEnemyIndex = ENTITYNUM_NONE;
  4256. }
  4257. else if (OnSameTeam(self, en))
  4258. {
  4259. self->client->ps.genericEnemyIndex = ENTITYNUM_NONE;
  4260. }
  4261. else
  4262. {
  4263. if (!InFront(en->client->ps.origin, self->client->ps.origin, self->client->ps.viewangles, 0.8f ))
  4264. {
  4265. self->client->ps.genericEnemyIndex = ENTITYNUM_NONE;
  4266. }
  4267. else if (!OrgVisible(self->client->ps.origin, en->client->ps.origin, self->s.number))
  4268. {
  4269. self->client->ps.genericEnemyIndex = ENTITYNUM_NONE;
  4270. }
  4271. }
  4272. }
  4273. if (self->client->ps.genericEnemyIndex == ENTITYNUM_NONE || self->client->ps.genericEnemyIndex == -1)
  4274. {
  4275. FindGenericEnemyIndex(self);
  4276. }
  4277. if (self->client->ps.genericEnemyIndex != ENTITYNUM_NONE && self->client->ps.genericEnemyIndex != -1)
  4278. {
  4279. en = &g_entities[self->client->ps.genericEnemyIndex];
  4280. VectorCopy(self->client->ps.origin, elevated);
  4281. elevated[2] += 40;
  4282. angle = ((level.time / 12) & 255) * (M_PI * 2) / 255; //magical numbers make magic happen
  4283. dir[0] = cos(angle) * 20;
  4284. dir[1] = sin(angle) * 20;
  4285. dir[2] = cos(angle) * 5;
  4286. VectorAdd(elevated, dir, org);
  4287. //org is now where the thing should be client-side because it uses the same time-based offset
  4288. if (self->client->ps.droneFireTime < level.time)
  4289. {
  4290. trap_Trace(&tr, org, NULL, NULL, en->client->ps.origin, -1, MASK_SOLID);
  4291. if (tr.fraction == 1 && !tr.startsolid && !tr.allsolid)
  4292. {
  4293. VectorSubtract(en->client->ps.origin, org, endir);
  4294. VectorNormalize(endir);
  4295. WP_FireGenericBlasterMissile(self, org, endir, 0, 15, 2000, MOD_BLASTER);
  4296. G_SoundAtLoc( org, CHAN_WEAPON, G_SoundIndex("sound/weapons/bryar/fire.wav") );
  4297. self->client->ps.droneFireTime = level.time + Q_irand(400, 700);
  4298. }
  4299. }
  4300. }
  4301. }
  4302. void HolocronUpdate(gentity_t *self)
  4303. { //keep holocron status updated in holocron mode
  4304. int i = 0;
  4305. int noHRank = 0;
  4306. if (noHRank < FORCE_LEVEL_0)
  4307. {
  4308. noHRank = FORCE_LEVEL_0;
  4309. }
  4310. if (noHRank > FORCE_LEVEL_3)
  4311. {
  4312. noHRank = FORCE_LEVEL_3;
  4313. }
  4314. trap_Cvar_Update(&g_maxHolocronCarry);
  4315. while (i < NUM_FORCE_POWERS)
  4316. {
  4317. if (self->client->ps.holocronsCarried[i])
  4318. { //carrying it, make sure we have the power
  4319. self->client->ps.holocronBits |= (1 << i);
  4320. self->client->ps.fd.forcePowersKnown |= (1 << i);
  4321. self->client->ps.fd.forcePowerLevel[i] = FORCE_LEVEL_3;
  4322. }
  4323. else
  4324. { //otherwise, make sure the power is cleared from us
  4325. self->client->ps.fd.forcePowerLevel[i] = 0;
  4326. if (self->client->ps.holocronBits & (1 << i))
  4327. {
  4328. self->client->ps.holocronBits -= (1 << i);
  4329. }
  4330. if ((self->client->ps.fd.forcePowersKnown & (1 << i)) && i != FP_LEVITATION && i != FP_SABER_OFFENSE)
  4331. {
  4332. self->client->ps.fd.forcePowersKnown -= (1 << i);
  4333. }
  4334. if ((self->client->ps.fd.forcePowersActive & (1 << i)) && i != FP_LEVITATION && i != FP_SABER_OFFENSE)
  4335. {
  4336. WP_ForcePowerStop(self, i);
  4337. }
  4338. if (i == FP_LEVITATION)
  4339. {
  4340. if (noHRank >= FORCE_LEVEL_1)
  4341. {
  4342. self->client->ps.fd.forcePowerLevel[i] = noHRank;
  4343. }
  4344. else
  4345. {
  4346. self->client->ps.fd.forcePowerLevel[i] = FORCE_LEVEL_1;
  4347. }
  4348. }
  4349. else if (i == FP_SABER_OFFENSE)
  4350. {
  4351. self->client->ps.fd.forcePowersKnown |= (1 << i);
  4352. if (noHRank >= FORCE_LEVEL_1)
  4353. {
  4354. self->client->ps.fd.forcePowerLevel[i] = noHRank;
  4355. }
  4356. else
  4357. {
  4358. self->client->ps.fd.forcePowerLevel[i] = FORCE_LEVEL_1;
  4359. }
  4360. }
  4361. else
  4362. {
  4363. self->client->ps.fd.forcePowerLevel[i] = FORCE_LEVEL_0;
  4364. }
  4365. }
  4366. i++;
  4367. }
  4368. if (HasSetSaberOnly())
  4369. { //if saberonly, we get these powers no matter what (still need the holocrons for level 3)
  4370. if (self->client->ps.fd.forcePowerLevel[FP_SABER_OFFENSE] < FORCE_LEVEL_1)
  4371. {
  4372. self->client->ps.fd.forcePowerLevel[FP_SABER_OFFENSE] = FORCE_LEVEL_1;
  4373. }
  4374. if (self->client->ps.fd.forcePowerLevel[FP_SABER_DEFENSE] < FORCE_LEVEL_1)
  4375. {
  4376. self->client->ps.fd.forcePowerLevel[FP_SABER_DEFENSE] = FORCE_LEVEL_1;
  4377. }
  4378. }
  4379. }
  4380. void JediMasterUpdate(gentity_t *self)
  4381. { //keep jedi master status updated for JM gametype
  4382. int i = 0;
  4383. trap_Cvar_Update(&g_maxHolocronCarry);
  4384. while (i < NUM_FORCE_POWERS)
  4385. {
  4386. if (self->client->ps.isJediMaster)
  4387. {
  4388. self->client->ps.fd.forcePowersKnown |= (1 << i);
  4389. self->client->ps.fd.forcePowerLevel[i] = FORCE_LEVEL_3;
  4390. if (i == FP_TEAM_HEAL || i == FP_TEAM_FORCE ||
  4391. i == FP_DRAIN || i == FP_ABSORB)
  4392. { //team powers are useless in JM, absorb is too because no one else has powers to absorb. Drain is just
  4393. //relatively useless in comparison, because its main intent is not to heal, but rather to cripple others
  4394. //by draining their force at the same time. And no one needs force in JM except the JM himself.
  4395. self->client->ps.fd.forcePowersKnown &= ~(1 << i);
  4396. self->client->ps.fd.forcePowerLevel[i] = 0;
  4397. }
  4398. if (i == FP_TELEPATHY)
  4399. { //this decision was made because level 3 mindtrick allows the JM to just hide too much, and no one else has force
  4400. //sight to counteract it. Since the JM himself is the focus of gameplay in this mode, having him hidden for large
  4401. //durations is indeed a bad thing.
  4402. self->client->ps.fd.forcePowerLevel[i] = FORCE_LEVEL_2;
  4403. }
  4404. }
  4405. else
  4406. {
  4407. if ((self->client->ps.fd.forcePowersKnown & (1 << i)) && i != FP_LEVITATION)
  4408. {
  4409. self->client->ps.fd.forcePowersKnown -= (1 << i);
  4410. }
  4411. if ((self->client->ps.fd.forcePowersActive & (1 << i)) && i != FP_LEVITATION)
  4412. {
  4413. WP_ForcePowerStop(self, i);
  4414. }
  4415. if (i == FP_LEVITATION)
  4416. {
  4417. self->client->ps.fd.forcePowerLevel[i] = FORCE_LEVEL_1;
  4418. }
  4419. else
  4420. {
  4421. self->client->ps.fd.forcePowerLevel[i] = FORCE_LEVEL_0;
  4422. }
  4423. }
  4424. i++;
  4425. }
  4426. }
  4427. qboolean WP_HasForcePowers( const playerState_t *ps )
  4428. {
  4429. int i;
  4430. if ( ps )
  4431. {
  4432. for ( i = 0; i < NUM_FORCE_POWERS; i++ )
  4433. {
  4434. if ( i == FP_LEVITATION )
  4435. {
  4436. if ( ps->fd.forcePowerLevel[i] > FORCE_LEVEL_1 )
  4437. {
  4438. return qtrue;
  4439. }
  4440. }
  4441. else if ( ps->fd.forcePowerLevel[i] > FORCE_LEVEL_0 )
  4442. {
  4443. return qtrue;
  4444. }
  4445. }
  4446. }
  4447. return qfalse;
  4448. }
  4449. //try a special roll getup move
  4450. qboolean G_SpecialRollGetup(gentity_t *self)
  4451. { //fixme: currently no knockdown will actually land you on your front... so froll's are pretty useless at the moment.
  4452. qboolean rolled = qfalse;
  4453. /*
  4454. if (self->client->ps.weapon != WP_SABER &&
  4455. self->client->ps.weapon != WP_MELEE)
  4456. { //can't do acrobatics without saber selected
  4457. return qfalse;
  4458. }
  4459. */
  4460. if (/*!self->client->pers.cmd.upmove &&*/
  4461. self->client->pers.cmd.rightmove > 0 &&
  4462. !self->client->pers.cmd.forwardmove)
  4463. {
  4464. G_SetAnim(self, &self->client->pers.cmd, SETANIM_BOTH, BOTH_GETUP_BROLL_R, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD, 0);
  4465. rolled = qtrue;
  4466. }
  4467. else if (/*!self->client->pers.cmd.upmove &&*/
  4468. self->client->pers.cmd.rightmove < 0 &&
  4469. !self->client->pers.cmd.forwardmove)
  4470. {
  4471. G_SetAnim(self, &self->client->pers.cmd, SETANIM_BOTH, BOTH_GETUP_BROLL_L, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD, 0);
  4472. rolled = qtrue;
  4473. }
  4474. else if (/*self->client->pers.cmd.upmove > 0 &&*/
  4475. !self->client->pers.cmd.rightmove &&
  4476. self->client->pers.cmd.forwardmove > 0)
  4477. {
  4478. G_SetAnim(self, &self->client->pers.cmd, SETANIM_BOTH, BOTH_GETUP_BROLL_F, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD, 0);
  4479. rolled = qtrue;
  4480. }
  4481. else if (/*self->client->pers.cmd.upmove > 0 &&*/
  4482. !self->client->pers.cmd.rightmove &&
  4483. self->client->pers.cmd.forwardmove < 0)
  4484. {
  4485. G_SetAnim(self, &self->client->pers.cmd, SETANIM_BOTH, BOTH_GETUP_BROLL_B, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD, 0);
  4486. rolled = qtrue;
  4487. }
  4488. else if (self->client->pers.cmd.upmove)
  4489. {
  4490. G_PreDefSound(self->client->ps.origin, PDSOUND_FORCEJUMP);
  4491. self->client->ps.forceDodgeAnim = 2;
  4492. self->client->ps.forceHandExtendTime = level.time + 500;
  4493. //self->client->ps.velocity[2] = 300;
  4494. }
  4495. if (rolled)
  4496. {
  4497. G_EntitySound( self, CHAN_VOICE, G_SoundIndex("*jump1.wav") );
  4498. }
  4499. return rolled;
  4500. }
  4501. void WP_ForcePowersUpdate( gentity_t *self, usercmd_t *ucmd )
  4502. {
  4503. qboolean usingForce = qfalse;
  4504. int i, holo, holoregen;
  4505. int prepower = 0;
  4506. //see if any force powers are running
  4507. if ( !self )
  4508. {
  4509. return;
  4510. }
  4511. if ( !self->client )
  4512. {
  4513. return;
  4514. }
  4515. if (self->client->ps.pm_flags & PMF_FOLLOW)
  4516. { //not a "real" game client, it's a spectator following someone
  4517. return;
  4518. }
  4519. if (self->client->sess.sessionTeam == TEAM_SPECTATOR)
  4520. {
  4521. return;
  4522. }
  4523. /*
  4524. if (self->client->ps.fd.saberAnimLevel > self->client->ps.fd.forcePowerLevel[FP_SABER_OFFENSE])
  4525. {
  4526. self->client->ps.fd.saberAnimLevel = self->client->ps.fd.forcePowerLevel[FP_SABER_OFFENSE];
  4527. }
  4528. else if (!self->client->ps.fd.saberAnimLevel)
  4529. {
  4530. self->client->ps.fd.saberAnimLevel = FORCE_LEVEL_1;
  4531. }
  4532. */
  4533. //The stance in relation to power level is no longer applicable with the crazy new akimbo/staff stances.
  4534. if (!self->client->ps.fd.saberAnimLevel)
  4535. {
  4536. self->client->ps.fd.saberAnimLevel = FORCE_LEVEL_1;
  4537. }
  4538. if (level.gametype != GT_SIEGE)
  4539. {
  4540. if (!(self->client->ps.fd.forcePowersKnown & (1 << FP_LEVITATION)))
  4541. {
  4542. self->client->ps.fd.forcePowersKnown |= (1 << FP_LEVITATION);
  4543. }
  4544. if (self->client->ps.fd.forcePowerLevel[FP_LEVITATION] < FORCE_LEVEL_1)
  4545. {
  4546. self->client->ps.fd.forcePowerLevel[FP_LEVITATION] = FORCE_LEVEL_1;
  4547. }
  4548. }
  4549. if (self->client->ps.fd.forcePowerSelected < 0)
  4550. { //bad
  4551. self->client->ps.fd.forcePowerSelected = 0;
  4552. }
  4553. if ( ((self->client->sess.selectedFP != self->client->ps.fd.forcePowerSelected) ||
  4554. (self->client->sess.saberLevel != self->client->ps.fd.saberAnimLevel)) &&
  4555. !(self->r.svFlags & SVF_BOT) )
  4556. {
  4557. if (self->client->sess.updateUITime < level.time)
  4558. { //a bit hackish, but we don't want the client to flood with userinfo updates if they rapidly cycle
  4559. //through their force powers or saber attack levels
  4560. self->client->sess.selectedFP = self->client->ps.fd.forcePowerSelected;
  4561. self->client->sess.saberLevel = self->client->ps.fd.saberAnimLevel;
  4562. }
  4563. }
  4564. if (!g_LastFrameTime)
  4565. {
  4566. g_LastFrameTime = level.time;
  4567. }
  4568. if (self->client->ps.forceHandExtend == HANDEXTEND_KNOCKDOWN)
  4569. {
  4570. self->client->ps.zoomFov = 0;
  4571. self->client->ps.zoomMode = 0;
  4572. self->client->ps.zoomLocked = qfalse;
  4573. self->client->ps.zoomTime = 0;
  4574. }
  4575. if (self->client->ps.forceHandExtend == HANDEXTEND_KNOCKDOWN &&
  4576. self->client->ps.forceHandExtendTime >= level.time)
  4577. {
  4578. self->client->ps.saberMove = 0;
  4579. self->client->ps.saberBlocking = 0;
  4580. self->client->ps.saberBlocked = 0;
  4581. self->client->ps.weaponTime = 0;
  4582. self->client->ps.weaponstate = WEAPON_READY;
  4583. }
  4584. else if (self->client->ps.forceHandExtend != HANDEXTEND_NONE &&
  4585. self->client->ps.forceHandExtendTime < level.time)
  4586. {
  4587. if (self->client->ps.forceHandExtend == HANDEXTEND_KNOCKDOWN &&
  4588. !self->client->ps.forceDodgeAnim)
  4589. {
  4590. if (self->health < 1 || (self->client->ps.eFlags & EF_DEAD))
  4591. {
  4592. self->client->ps.forceHandExtend = HANDEXTEND_NONE;
  4593. }
  4594. else if (G_SpecialRollGetup(self))
  4595. {
  4596. self->client->ps.forceHandExtend = HANDEXTEND_NONE;
  4597. }
  4598. else
  4599. { //hmm.. ok.. no more getting up on your own, you've gotta push something, unless..
  4600. if ((level.time-self->client->ps.forceHandExtendTime) > 4000)
  4601. { //4 seconds elapsed, I guess they're too dumb to push something to get up!
  4602. if (self->client->pers.cmd.upmove &&
  4603. self->client->ps.fd.forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_1)
  4604. { //force getup
  4605. G_PreDefSound(self->client->ps.origin, PDSOUND_FORCEJUMP);
  4606. self->client->ps.forceDodgeAnim = 2;
  4607. self->client->ps.forceHandExtendTime = level.time + 500;
  4608. //self->client->ps.velocity[2] = 400;
  4609. }
  4610. else if (self->client->ps.quickerGetup)
  4611. {
  4612. G_EntitySound( self, CHAN_VOICE, G_SoundIndex("*jump1.wav") );
  4613. self->client->ps.forceDodgeAnim = 3;
  4614. self->client->ps.forceHandExtendTime = level.time + 500;
  4615. self->client->ps.velocity[2] = 300;
  4616. }
  4617. else
  4618. {
  4619. self->client->ps.forceDodgeAnim = 1;
  4620. self->client->ps.forceHandExtendTime = level.time + 1000;
  4621. }
  4622. }
  4623. }
  4624. self->client->ps.quickerGetup = qfalse;
  4625. }
  4626. else if (self->client->ps.forceHandExtend == HANDEXTEND_POSTTHROWN)
  4627. {
  4628. if (self->health < 1 || (self->client->ps.eFlags & EF_DEAD))
  4629. {
  4630. self->client->ps.forceHandExtend = HANDEXTEND_NONE;
  4631. }
  4632. else if (self->client->ps.groundEntityNum != ENTITYNUM_NONE && !self->client->ps.forceDodgeAnim)
  4633. {
  4634. self->client->ps.forceDodgeAnim = 1;
  4635. self->client->ps.forceHandExtendTime = level.time + 1000;
  4636. G_EntitySound( self, CHAN_VOICE, G_SoundIndex("*jump1.wav") );
  4637. self->client->ps.velocity[2] = 100;
  4638. }
  4639. else if (!self->client->ps.forceDodgeAnim)
  4640. {
  4641. self->client->ps.forceHandExtendTime = level.time + 100;
  4642. }
  4643. else
  4644. {
  4645. self->client->ps.forceHandExtend = HANDEXTEND_WEAPONREADY;
  4646. }
  4647. }
  4648. else
  4649. {
  4650. self->client->ps.forceHandExtend = HANDEXTEND_WEAPONREADY;
  4651. }
  4652. }
  4653. if (level.gametype == GT_HOLOCRON)
  4654. {
  4655. HolocronUpdate(self);
  4656. }
  4657. if (level.gametype == GT_JEDIMASTER)
  4658. {
  4659. JediMasterUpdate(self);
  4660. }
  4661. SeekerDroneUpdate(self);
  4662. if (self->client->ps.powerups[PW_FORCE_BOON])
  4663. {
  4664. prepower = self->client->ps.fd.forcePower;
  4665. }
  4666. if (self && self->client && (BG_HasYsalamiri(level.gametype, &self->client->ps) ||
  4667. self->client->ps.fd.forceDeactivateAll || self->client->tempSpectate >= level.time))
  4668. { //has ysalamiri.. or we want to forcefully stop all his active powers
  4669. i = 0;
  4670. while (i < NUM_FORCE_POWERS)
  4671. {
  4672. if ((self->client->ps.fd.forcePowersActive & (1 << i)) && i != FP_LEVITATION)
  4673. {
  4674. WP_ForcePowerStop(self, i);
  4675. }
  4676. i++;
  4677. }
  4678. if (self->client->tempSpectate >= level.time)
  4679. {
  4680. self->client->ps.fd.forcePower = 100;
  4681. self->client->ps.fd.forceRageRecoveryTime = 0;
  4682. }
  4683. self->client->ps.fd.forceDeactivateAll = 0;
  4684. if (self->client->ps.fd.forceJumpCharge)
  4685. {
  4686. G_MuteSound(self->client->ps.fd.killSoundEntIndex[TRACK_CHANNEL_1-50], CHAN_VOICE);
  4687. self->client->ps.fd.forceJumpCharge = 0;
  4688. }
  4689. }
  4690. else
  4691. { //otherwise just do a check through them all to see if they need to be stopped for any reason.
  4692. i = 0;
  4693. while (i < NUM_FORCE_POWERS)
  4694. {
  4695. if ((self->client->ps.fd.forcePowersActive & (1 << i)) && i != FP_LEVITATION &&
  4696. !BG_CanUseFPNow(level.gametype, &self->client->ps, level.time, i))
  4697. {
  4698. WP_ForcePowerStop(self, i);
  4699. }
  4700. i++;
  4701. }
  4702. }
  4703. i = 0;
  4704. if (self->client->ps.powerups[PW_FORCE_ENLIGHTENED_LIGHT] || self->client->ps.powerups[PW_FORCE_ENLIGHTENED_DARK])
  4705. { //enlightenment
  4706. if (!self->client->ps.fd.forceUsingAdded)
  4707. {
  4708. i = 0;
  4709. while (i < NUM_FORCE_POWERS)
  4710. {
  4711. self->client->ps.fd.forcePowerBaseLevel[i] = self->client->ps.fd.forcePowerLevel[i];
  4712. if (!forcePowerDarkLight[i] ||
  4713. self->client->ps.fd.forceSide == forcePowerDarkLight[i])
  4714. {
  4715. self->client->ps.fd.forcePowerLevel[i] = FORCE_LEVEL_3;
  4716. self->client->ps.fd.forcePowersKnown |= (1 << i);
  4717. }
  4718. i++;
  4719. }
  4720. self->client->ps.fd.forceUsingAdded = 1;
  4721. }
  4722. }
  4723. else if (self->client->ps.fd.forceUsingAdded)
  4724. { //we don't have enlightenment but we're still using enlightened powers, so clear them back to how they should be.
  4725. i = 0;
  4726. while (i < NUM_FORCE_POWERS)
  4727. {
  4728. self->client->ps.fd.forcePowerLevel[i] = self->client->ps.fd.forcePowerBaseLevel[i];
  4729. if (!self->client->ps.fd.forcePowerLevel[i])
  4730. {
  4731. if (self->client->ps.fd.forcePowersActive & (1 << i))
  4732. {
  4733. WP_ForcePowerStop(self, i);
  4734. }
  4735. self->client->ps.fd.forcePowersKnown &= ~(1 << i);
  4736. }
  4737. i++;
  4738. }
  4739. self->client->ps.fd.forceUsingAdded = 0;
  4740. }
  4741. i = 0;
  4742. if (!(self->client->ps.fd.forcePowersActive & (1 << FP_TELEPATHY)))
  4743. { //clear the mindtrick index values
  4744. self->client->ps.fd.forceMindtrickTargetIndex = 0;
  4745. self->client->ps.fd.forceMindtrickTargetIndex2 = 0;
  4746. self->client->ps.fd.forceMindtrickTargetIndex3 = 0;
  4747. self->client->ps.fd.forceMindtrickTargetIndex4 = 0;
  4748. }
  4749. if (self->health < 1)
  4750. {
  4751. self->client->ps.fd.forceGripBeingGripped = 0;
  4752. }
  4753. if (self->client->ps.fd.forceGripBeingGripped > level.time)
  4754. {
  4755. self->client->ps.fd.forceGripCripple = 1;
  4756. //keep the saber off during this period
  4757. if (self->client->ps.weapon == WP_SABER && !self->client->ps.saberHolstered)
  4758. {
  4759. Cmd_ToggleSaber_f(self);
  4760. }
  4761. }
  4762. else
  4763. {
  4764. self->client->ps.fd.forceGripCripple = 0;
  4765. }
  4766. if (self->client->ps.fd.forceJumpSound)
  4767. {
  4768. G_PreDefSound(self->client->ps.origin, PDSOUND_FORCEJUMP);
  4769. self->client->ps.fd.forceJumpSound = 0;
  4770. }
  4771. if (self->client->ps.fd.forceGripCripple)
  4772. {
  4773. if (self->client->ps.fd.forceGripSoundTime < level.time)
  4774. {
  4775. G_PreDefSound(self->client->ps.origin, PDSOUND_FORCEGRIP);
  4776. self->client->ps.fd.forceGripSoundTime = level.time + 1000;
  4777. }
  4778. }
  4779. if (self->client->ps.fd.forcePowersActive & (1 << FP_SPEED))
  4780. {
  4781. self->client->ps.powerups[PW_SPEED] = level.time + 100;
  4782. }
  4783. if ( self->health <= 0 )
  4784. {//if dead, deactivate any active force powers
  4785. for ( i = 0; i < NUM_FORCE_POWERS; i++ )
  4786. {
  4787. if ( self->client->ps.fd.forcePowerDuration[i] || (self->client->ps.fd.forcePowersActive&( 1 << i )) )
  4788. {
  4789. WP_ForcePowerStop( self, (forcePowers_t)i );
  4790. self->client->ps.fd.forcePowerDuration[i] = 0;
  4791. }
  4792. }
  4793. goto powersetcheck;
  4794. }
  4795. if (self->client->ps.groundEntityNum != ENTITYNUM_NONE)
  4796. {
  4797. self->client->fjDidJump = qfalse;
  4798. }
  4799. if (self->client->ps.fd.forceJumpCharge && self->client->ps.groundEntityNum == ENTITYNUM_NONE && self->client->fjDidJump)
  4800. { //this was for the "charge" jump method... I guess
  4801. if (ucmd->upmove < 10 && (!(ucmd->buttons & BUTTON_FORCEPOWER) || self->client->ps.fd.forcePowerSelected != FP_LEVITATION))
  4802. {
  4803. G_MuteSound(self->client->ps.fd.killSoundEntIndex[TRACK_CHANNEL_1-50], CHAN_VOICE);
  4804. self->client->ps.fd.forceJumpCharge = 0;
  4805. }
  4806. }
  4807. #ifndef METROID_JUMP
  4808. else if ( (ucmd->upmove > 10) && (self->client->ps.pm_flags & PMF_JUMP_HELD) && self->client->ps.groundTime && (level.time - self->client->ps.groundTime) > 150 && !BG_HasYsalamiri(level.gametype, &self->client->ps) && BG_CanUseFPNow(level.gametype, &self->client->ps, level.time, FP_LEVITATION) )
  4809. {//just charging up
  4810. ForceJumpCharge( self, ucmd );
  4811. usingForce = qtrue;
  4812. }
  4813. else if (ucmd->upmove < 10 && self->client->ps.groundEntityNum == ENTITYNUM_NONE && self->client->ps.fd.forceJumpCharge)
  4814. {
  4815. self->client->ps.pm_flags &= ~(PMF_JUMP_HELD);
  4816. }
  4817. #endif
  4818. if (!(self->client->ps.pm_flags & PMF_JUMP_HELD) && self->client->ps.fd.forceJumpCharge)
  4819. {
  4820. if (!(ucmd->buttons & BUTTON_FORCEPOWER) ||
  4821. self->client->ps.fd.forcePowerSelected != FP_LEVITATION)
  4822. {
  4823. if (WP_DoSpecificPower( self, ucmd, FP_LEVITATION ))
  4824. {
  4825. usingForce = qtrue;
  4826. }
  4827. }
  4828. }
  4829. if ( ucmd->buttons & BUTTON_FORCEGRIP )
  4830. { //grip is one of the powers with its own button.. if it's held, call the specific grip power function.
  4831. if (WP_DoSpecificPower( self, ucmd, FP_GRIP ))
  4832. {
  4833. usingForce = qtrue;
  4834. }
  4835. else
  4836. { //don't let recharge even if the grip misses if the player still has the button down
  4837. usingForce = qtrue;
  4838. }
  4839. }
  4840. else
  4841. { //see if we're using it generically.. if not, stop.
  4842. if (self->client->ps.fd.forcePowersActive & (1 << FP_GRIP))
  4843. {
  4844. if (!(ucmd->buttons & BUTTON_FORCEPOWER) || self->client->ps.fd.forcePowerSelected != FP_GRIP)
  4845. {
  4846. WP_ForcePowerStop(self, FP_GRIP);
  4847. }
  4848. }
  4849. }
  4850. if ( ucmd->buttons & BUTTON_FORCE_LIGHTNING )
  4851. { //lightning
  4852. WP_DoSpecificPower(self, ucmd, FP_LIGHTNING);
  4853. usingForce = qtrue;
  4854. }
  4855. else
  4856. { //see if we're using it generically.. if not, stop.
  4857. if (self->client->ps.fd.forcePowersActive & (1 << FP_LIGHTNING))
  4858. {
  4859. if (!(ucmd->buttons & BUTTON_FORCEPOWER) || self->client->ps.fd.forcePowerSelected != FP_LIGHTNING)
  4860. {
  4861. WP_ForcePowerStop(self, FP_LIGHTNING);
  4862. }
  4863. }
  4864. }
  4865. if ( ucmd->buttons & BUTTON_FORCE_DRAIN )
  4866. { //drain
  4867. WP_DoSpecificPower(self, ucmd, FP_DRAIN);
  4868. usingForce = qtrue;
  4869. }
  4870. else
  4871. { //see if we're using it generically.. if not, stop.
  4872. if (self->client->ps.fd.forcePowersActive & (1 << FP_DRAIN))
  4873. {
  4874. if (!(ucmd->buttons & BUTTON_FORCEPOWER) || self->client->ps.fd.forcePowerSelected != FP_DRAIN)
  4875. {
  4876. WP_ForcePowerStop(self, FP_DRAIN);
  4877. }
  4878. }
  4879. }
  4880. if ( (ucmd->buttons & BUTTON_FORCEPOWER) &&
  4881. BG_CanUseFPNow(level.gametype, &self->client->ps, level.time, self->client->ps.fd.forcePowerSelected))
  4882. {
  4883. if (self->client->ps.fd.forcePowerSelected == FP_LEVITATION)
  4884. {
  4885. ForceJumpCharge( self, ucmd );
  4886. usingForce = qtrue;
  4887. }
  4888. else if (WP_DoSpecificPower( self, ucmd, self->client->ps.fd.forcePowerSelected ))
  4889. {
  4890. usingForce = qtrue;
  4891. }
  4892. else if (self->client->ps.fd.forcePowerSelected == FP_GRIP)
  4893. {
  4894. usingForce = qtrue;
  4895. }
  4896. }
  4897. else
  4898. {
  4899. self->client->ps.fd.forceButtonNeedRelease = 0;
  4900. }
  4901. for ( i = 0; i < NUM_FORCE_POWERS; i++ )
  4902. {
  4903. if ( self->client->ps.fd.forcePowerDuration[i] )
  4904. {
  4905. if ( self->client->ps.fd.forcePowerDuration[i] < level.time )
  4906. {
  4907. if ( (self->client->ps.fd.forcePowersActive&( 1 << i )) )
  4908. {//turn it off
  4909. WP_ForcePowerStop( self, (forcePowers_t)i );
  4910. }
  4911. self->client->ps.fd.forcePowerDuration[i] = 0;
  4912. }
  4913. }
  4914. if ( (self->client->ps.fd.forcePowersActive&( 1 << i )) )
  4915. {
  4916. usingForce = qtrue;
  4917. WP_ForcePowerRun( self, (forcePowers_t)i, ucmd );
  4918. }
  4919. }
  4920. if ( !(self->client->ps.fd.forcePowersActive & (1<<FP_DRAIN)) )
  4921. self->client->force.drainDebounce = level.time;
  4922. if ( !(self->client->ps.fd.forcePowersActive & (1<<FP_LIGHTNING)) )
  4923. self->client->force.lightningDebounce = level.time;
  4924. if ( self->client->ps.saberInFlight && self->client->ps.saberEntityNum )
  4925. {//don't regen force power while throwing saber
  4926. if ( self->client->ps.saberEntityNum < ENTITYNUM_NONE && self->client->ps.saberEntityNum > 0 )//player is 0
  4927. {//
  4928. if ( &g_entities[self->client->ps.saberEntityNum] != NULL && g_entities[self->client->ps.saberEntityNum].s.pos.trType == TR_LINEAR )
  4929. {//fell to the ground and we're trying to pull it back
  4930. usingForce = qtrue;
  4931. }
  4932. }
  4933. }
  4934. if ( !self->client->ps.fd.forcePowersActive || self->client->ps.fd.forcePowersActive == (1 << FP_DRAIN) )
  4935. {//when not using the force, regenerate at 1 point per half second
  4936. if ( !self->client->ps.saberInFlight && (self->client->ps.weapon != WP_SABER || !BG_SaberInSpecial(self->client->ps.saberMove)) )
  4937. {
  4938. while ( self->client->ps.fd.forcePowerRegenDebounceTime < level.time )
  4939. {
  4940. if (level.gametype != GT_HOLOCRON || g_maxHolocronCarry.value)
  4941. {
  4942. if ( self->client->ps.powerups[PW_FORCE_BOON] )
  4943. WP_ForcePowerRegenerate( self, 6 );
  4944. else if ( self->client->ps.isJediMaster && level.gametype == GT_JEDIMASTER )
  4945. WP_ForcePowerRegenerate( self, 4 ); //jedi master regenerates 4 times as fast
  4946. else
  4947. WP_ForcePowerRegenerate( self, 0 );
  4948. }
  4949. else
  4950. { //regenerate based on the number of holocrons carried
  4951. holoregen = 0;
  4952. holo = 0;
  4953. while (holo < NUM_FORCE_POWERS)
  4954. {
  4955. if (self->client->ps.holocronsCarried[holo])
  4956. holoregen++;
  4957. holo++;
  4958. }
  4959. WP_ForcePowerRegenerate(self, holoregen);
  4960. }
  4961. if (level.gametype == GT_SIEGE)
  4962. {
  4963. if ( self->client->holdingObjectiveItem && g_entities[self->client->holdingObjectiveItem].inuse && g_entities[self->client->holdingObjectiveItem].genericValue15 )
  4964. self->client->ps.fd.forcePowerRegenDebounceTime += 7000; //1 point per 7 seconds.. super slow
  4965. else if (self->client->siegeClass != -1 && (bgSiegeClasses[self->client->siegeClass].classflags & (1<<CFL_FASTFORCEREGEN)))
  4966. self->client->ps.fd.forcePowerRegenDebounceTime += max(g_forceRegenTime.integer*0.2, 1); //if this is siege and our player class has the fast force regen ability, then recharge with 1/5th the usual delay
  4967. else
  4968. self->client->ps.fd.forcePowerRegenDebounceTime += max(g_forceRegenTime.integer, 1);
  4969. }
  4970. else
  4971. {
  4972. if ( level.gametype == GT_POWERDUEL && self->client->sess.duelTeam == DUELTEAM_LONE )
  4973. {
  4974. if ( duel_fraglimit.integer )
  4975. self->client->ps.fd.forcePowerRegenDebounceTime += max(g_forceRegenTime.integer * (0.6 + (.3 * (float)self->client->sess.wins / (float)duel_fraglimit.integer)), 1);
  4976. else
  4977. self->client->ps.fd.forcePowerRegenDebounceTime += max(g_forceRegenTime.integer*0.7, 1);
  4978. }
  4979. else
  4980. self->client->ps.fd.forcePowerRegenDebounceTime += max(g_forceRegenTime.integer, 1);
  4981. }
  4982. }
  4983. }
  4984. }
  4985. else
  4986. self->client->ps.fd.forcePowerRegenDebounceTime = level.time;
  4987. powersetcheck:
  4988. if (prepower && self->client->ps.fd.forcePower < prepower)
  4989. {
  4990. int dif = ((prepower - self->client->ps.fd.forcePower)/2);
  4991. if (dif < 1)
  4992. {
  4993. dif = 1;
  4994. }
  4995. self->client->ps.fd.forcePower = (prepower-dif);
  4996. }
  4997. }
  4998. qboolean Jedi_DodgeEvasion( gentity_t *self, gentity_t *shooter, trace_t *tr, int hitLoc )
  4999. {
  5000. int dodgeAnim = -1;
  5001. if ( !self || !self->client || self->health <= 0 )
  5002. {
  5003. return qfalse;
  5004. }
  5005. if (!g_forceDodge.integer)
  5006. {
  5007. return qfalse;
  5008. }
  5009. if (g_forceDodge.integer != 2)
  5010. {
  5011. if (!(self->client->ps.fd.forcePowersActive & (1 << FP_SEE)))
  5012. {
  5013. return qfalse;
  5014. }
  5015. }
  5016. if ( self->client->ps.groundEntityNum == ENTITYNUM_NONE )
  5017. {//can't dodge in mid-air
  5018. return qfalse;
  5019. }
  5020. if ( self->client->ps.weaponTime > 0 || self->client->ps.forceHandExtend != HANDEXTEND_NONE )
  5021. {//in some effect that stops me from moving on my own
  5022. return qfalse;
  5023. }
  5024. if (g_forceDodge.integer == 2)
  5025. {
  5026. if (self->client->ps.fd.forcePowersActive)
  5027. { //for now just don't let us dodge if we're using a force power at all
  5028. return qfalse;
  5029. }
  5030. }
  5031. if (g_forceDodge.integer == 2)
  5032. {
  5033. if ( !WP_ForcePowerUsable( self, FP_SPEED ) )
  5034. {//make sure we have it and have enough force power
  5035. return qfalse;
  5036. }
  5037. }
  5038. if (g_forceDodge.integer == 2)
  5039. {
  5040. if ( Q_irand( 1, 7 ) > self->client->ps.fd.forcePowerLevel[FP_SPEED] )
  5041. {//more likely to fail on lower force speed level
  5042. return qfalse;
  5043. }
  5044. }
  5045. else
  5046. {
  5047. //We now dodge all the time, but only on level 3
  5048. if (self->client->ps.fd.forcePowerLevel[FP_SEE] < FORCE_LEVEL_3)
  5049. {//more likely to fail on lower force sight level
  5050. return qfalse;
  5051. }
  5052. }
  5053. switch( hitLoc )
  5054. {
  5055. case HL_NONE:
  5056. return qfalse;
  5057. break;
  5058. case HL_FOOT_RT:
  5059. case HL_FOOT_LT:
  5060. case HL_LEG_RT:
  5061. case HL_LEG_LT:
  5062. return qfalse;
  5063. case HL_BACK_RT:
  5064. dodgeAnim = BOTH_DODGE_FL;
  5065. break;
  5066. case HL_CHEST_RT:
  5067. dodgeAnim = BOTH_DODGE_FR;
  5068. break;
  5069. case HL_BACK_LT:
  5070. dodgeAnim = BOTH_DODGE_FR;
  5071. break;
  5072. case HL_CHEST_LT:
  5073. dodgeAnim = BOTH_DODGE_FR;
  5074. break;
  5075. case HL_BACK:
  5076. case HL_CHEST:
  5077. case HL_WAIST:
  5078. dodgeAnim = BOTH_DODGE_FL;
  5079. break;
  5080. case HL_ARM_RT:
  5081. case HL_HAND_RT:
  5082. dodgeAnim = BOTH_DODGE_L;
  5083. break;
  5084. case HL_ARM_LT:
  5085. case HL_HAND_LT:
  5086. dodgeAnim = BOTH_DODGE_R;
  5087. break;
  5088. case HL_HEAD:
  5089. dodgeAnim = BOTH_DODGE_FL;
  5090. break;
  5091. default:
  5092. return qfalse;
  5093. }
  5094. if ( dodgeAnim != -1 )
  5095. {
  5096. //Our own happy way of forcing an anim:
  5097. self->client->ps.forceHandExtend = HANDEXTEND_DODGE;
  5098. self->client->ps.forceDodgeAnim = dodgeAnim;
  5099. self->client->ps.forceHandExtendTime = level.time + 300;
  5100. self->client->ps.powerups[PW_SPEEDBURST] = level.time + 100;
  5101. if (g_forceDodge.integer == 2)
  5102. {
  5103. ForceSpeed( self, 500 );
  5104. }
  5105. else
  5106. {
  5107. G_Sound( self, CHAN_BODY, G_SoundIndex("sound/weapons/force/speed.wav") );
  5108. }
  5109. return qtrue;
  5110. }
  5111. return qfalse;
  5112. }