codemp/game/w_force.c

Go to the documentation of this file.
00001 //#include "g_local.h"
00002 #include "b_local.h"
00003 #include "w_saber.h"
00004 #include "ai_main.h"
00005 #include "../ghoul2/G2.h"
00006 
00007 #define METROID_JUMP 1
00008 
00009 //NEEDED FOR MIND-TRICK on NPCS=========================================================
00010 extern void NPC_PlayConfusionSound( gentity_t *self );
00011 extern void NPC_Jedi_PlayConfusionSound( gentity_t *self );
00012 extern void NPC_UseResponse( gentity_t *self, gentity_t *user, qboolean useWhenDone );
00013 //NEEDED FOR MIND-TRICK on NPCS=========================================================
00014 extern void Jedi_Decloak( gentity_t *self );
00015 
00016 extern vmCvar_t         g_saberRestrictForce;
00017 
00018 #include "../namespace_begin.h"
00019 extern qboolean BG_FullBodyTauntAnim( int anim );
00020 #include "../namespace_end.h"
00021 
00022 extern bot_state_t *botstates[MAX_CLIENTS];
00023 
00024 int speedLoopSound = 0;
00025  
00026 int rageLoopSound = 0;
00027 
00028 int protectLoopSound = 0;
00029 
00030 int absorbLoopSound = 0;
00031 
00032 int seeLoopSound = 0;
00033 
00034 int     ysalamiriLoopSound = 0;
00035 
00036 #define FORCE_VELOCITY_DAMAGE 0
00037 
00038 int ForceShootDrain( gentity_t *self );
00039 
00040 gentity_t *G_PreDefSound(vec3_t org, int pdSound)
00041 {
00042         gentity_t       *te;
00043 
00044         te = G_TempEntity( org, EV_PREDEFSOUND );
00045         te->s.eventParm = pdSound;
00046         VectorCopy(org, te->s.origin);
00047 
00048         return te;
00049 }
00050 
00051 const int forcePowerMinRank[NUM_FORCE_POWER_LEVELS][NUM_FORCE_POWERS] = //0 == neutral
00052 {
00053         {
00054                 999,//FP_HEAL,//instant
00055                 999,//FP_LEVITATION,//hold/duration
00056                 999,//FP_SPEED,//duration
00057                 999,//FP_PUSH,//hold/duration
00058                 999,//FP_PULL,//hold/duration
00059                 999,//FP_TELEPATHY,//instant
00060                 999,//FP_GRIP,//hold/duration
00061                 999,//FP_LIGHTNING,//hold/duration
00062                 999,//FP_RAGE,//duration
00063                 999,//FP_PROTECT,//duration
00064                 999,//FP_ABSORB,//duration
00065                 999,//FP_TEAM_HEAL,//instant
00066                 999,//FP_TEAM_FORCE,//instant
00067                 999,//FP_DRAIN,//hold/duration
00068                 999,//FP_SEE,//duration
00069                 999,//FP_SABER_OFFENSE,
00070                 999,//FP_SABER_DEFENSE,
00071                 999//FP_SABERTHROW,
00072                 //NUM_FORCE_POWERS
00073         },
00074         {
00075                 10,//FP_HEAL,//instant
00076                 0,//FP_LEVITATION,//hold/duration
00077                 0,//FP_SPEED,//duration
00078                 0,//FP_PUSH,//hold/duration
00079                 0,//FP_PULL,//hold/duration
00080                 10,//FP_TELEPATHY,//instant
00081                 15,//FP_GRIP,//hold/duration
00082                 10,//FP_LIGHTNING,//hold/duration
00083                 15,//FP_RAGE,//duration
00084                 15,//FP_PROTECT,//duration
00085                 15,//FP_ABSORB,//duration
00086                 10,//FP_TEAM_HEAL,//instant
00087                 10,//FP_TEAM_FORCE,//instant
00088                 10,//FP_DRAIN,//hold/duration
00089                 5,//FP_SEE,//duration
00090                 0,//FP_SABER_OFFENSE,
00091                 0,//FP_SABER_DEFENSE,
00092                 0//FP_SABERTHROW,
00093                 //NUM_FORCE_POWERS
00094         },
00095         {
00096                 10,//FP_HEAL,//instant
00097                 0,//FP_LEVITATION,//hold/duration
00098                 0,//FP_SPEED,//duration
00099                 0,//FP_PUSH,//hold/duration
00100                 0,//FP_PULL,//hold/duration
00101                 10,//FP_TELEPATHY,//instant
00102                 15,//FP_GRIP,//hold/duration
00103                 10,//FP_LIGHTNING,//hold/duration
00104                 15,//FP_RAGE,//duration
00105                 15,//FP_PROTECT,//duration
00106                 15,//FP_ABSORB,//duration
00107                 10,//FP_TEAM_HEAL,//instant
00108                 10,//FP_TEAM_FORCE,//instant
00109                 10,//FP_DRAIN,//hold/duration
00110                 5,//FP_SEE,//duration
00111                 5,//FP_SABER_OFFENSE,
00112                 5,//FP_SABER_DEFENSE,
00113                 5//FP_SABERTHROW,
00114                 //NUM_FORCE_POWERS
00115         },
00116         {
00117                 10,//FP_HEAL,//instant
00118                 0,//FP_LEVITATION,//hold/duration
00119                 0,//FP_SPEED,//duration
00120                 0,//FP_PUSH,//hold/duration
00121                 0,//FP_PULL,//hold/duration
00122                 10,//FP_TELEPATHY,//instant
00123                 15,//FP_GRIP,//hold/duration
00124                 10,//FP_LIGHTNING,//hold/duration
00125                 15,//FP_RAGE,//duration
00126                 15,//FP_PROTECT,//duration
00127                 15,//FP_ABSORB,//duration
00128                 10,//FP_TEAM_HEAL,//instant
00129                 10,//FP_TEAM_FORCE,//instant
00130                 10,//FP_DRAIN,//hold/duration
00131                 5,//FP_SEE,//duration
00132                 10,//FP_SABER_OFFENSE,
00133                 10,//FP_SABER_DEFENSE,
00134                 10//FP_SABERTHROW,
00135                 //NUM_FORCE_POWERS
00136         }
00137 };
00138 
00139 const int mindTrickTime[NUM_FORCE_POWER_LEVELS] =
00140 {
00141         0,//none
00142         5000,
00143         10000,
00144         15000
00145 };
00146 
00147 void WP_InitForcePowers( gentity_t *ent )
00148 {
00149         int i;
00150         int i_r;
00151         int maxRank = g_maxForceRank.integer;
00152         qboolean warnClient = qfalse;
00153         qboolean warnClientLimit = qfalse;
00154         char userinfo[MAX_INFO_STRING];
00155         char forcePowers[256];
00156         char readBuf[256];
00157         int lastFPKnown = -1;
00158         qboolean didEvent = qfalse;
00159 
00160         if (!maxRank)
00161         { //if server has no max rank, default to max (50)
00162                 maxRank = FORCE_MASTERY_JEDI_MASTER;
00163         }
00164         else if (maxRank >= NUM_FORCE_MASTERY_LEVELS)
00165         {//ack, prevent user from being dumb
00166                 maxRank = FORCE_MASTERY_JEDI_MASTER;
00167                 trap_Cvar_Set( "g_maxForceRank", va("%i", maxRank) );
00168         }
00169 
00170         /*
00171         if (g_forcePowerDisable.integer)
00172         {
00173                 maxRank = FORCE_MASTERY_UNINITIATED;
00174         }
00175         */
00176         //rww - don't do this
00177 
00178         if ( !ent || !ent->client )
00179         {
00180                 return;
00181         }
00182 
00183         ent->client->ps.fd.saberAnimLevel = ent->client->sess.saberLevel;
00184 
00185         if (ent->client->ps.fd.saberAnimLevel < FORCE_LEVEL_1 ||
00186                 ent->client->ps.fd.saberAnimLevel > FORCE_LEVEL_3)
00187         {
00188                 ent->client->ps.fd.saberAnimLevel = FORCE_LEVEL_1;
00189         }
00190 
00191         if (!speedLoopSound)
00192         { //so that the client configstring is already modified with this when we need it
00193                 speedLoopSound = G_SoundIndex("sound/weapons/force/speedloop.wav");
00194         }
00195 
00196         if (!rageLoopSound)
00197         {
00198                 rageLoopSound = G_SoundIndex("sound/weapons/force/rageloop.wav");
00199         }
00200 
00201         if (!absorbLoopSound)
00202         {
00203                 absorbLoopSound = G_SoundIndex("sound/weapons/force/absorbloop.wav");
00204         }
00205 
00206         if (!protectLoopSound)
00207         {
00208                 protectLoopSound = G_SoundIndex("sound/weapons/force/protectloop.wav");
00209         }
00210 
00211         if (!seeLoopSound)
00212         {
00213                 seeLoopSound = G_SoundIndex("sound/weapons/force/seeloop.wav");
00214         }
00215 
00216         if (!ysalamiriLoopSound)
00217         {
00218                 ysalamiriLoopSound = G_SoundIndex("sound/player/nullifyloop.wav");
00219         }
00220 
00221         if (ent->s.eType == ET_NPC)
00222         { //just stop here then.
00223                 return;
00224         }
00225 
00226         i = 0;
00227         while (i < NUM_FORCE_POWERS)
00228         {
00229                 ent->client->ps.fd.forcePowerLevel[i] = 0;
00230                 ent->client->ps.fd.forcePowersKnown &= ~(1 << i);
00231                 i++;
00232         }
00233 
00234         ent->client->ps.fd.forcePowerSelected = -1;
00235 
00236         ent->client->ps.fd.forceSide = 0;
00237 
00238         if (g_gametype.integer == GT_SIEGE &&
00239                 ent->client->siegeClass != -1)
00240         { //Then use the powers for this class, and skip all this nonsense.
00241                 i = 0;
00242 
00243                 while (i < NUM_FORCE_POWERS)
00244                 {
00245                         ent->client->ps.fd.forcePowerLevel[i] = bgSiegeClasses[ent->client->siegeClass].forcePowerLevels[i];
00246 
00247                         if (!ent->client->ps.fd.forcePowerLevel[i])
00248                         {
00249                                 ent->client->ps.fd.forcePowersKnown &= ~(1 << i);
00250                         }
00251                         else
00252                         {
00253                                 ent->client->ps.fd.forcePowersKnown |= (1 << i);
00254                         }
00255                         i++;
00256                 }
00257 
00258                 if (!ent->client->sess.setForce)
00259                 {
00260                         //bring up the class selection menu
00261                         trap_SendServerCommand(ent-g_entities, "scl");
00262                 }
00263                 ent->client->sess.setForce = qtrue;
00264 
00265                 return;
00266         }
00267 
00268         if (ent->s.eType == ET_NPC && ent->s.number >= MAX_CLIENTS)
00269         { //rwwFIXMEFIXME: Temp
00270                 strcpy(userinfo, "forcepowers\\7-1-333003000313003120");
00271         }
00272         else
00273         {
00274                 trap_GetUserinfo( ent->s.number, userinfo, sizeof( userinfo ) );
00275         }
00276 
00277         Q_strncpyz( forcePowers, Info_ValueForKey (userinfo, "forcepowers"), sizeof( forcePowers ) );
00278 
00279         if ( (ent->r.svFlags & SVF_BOT) && botstates[ent->s.number] )
00280         { //if it's a bot just copy the info directly from its personality
00281                 Com_sprintf(forcePowers, sizeof(forcePowers), "%s\0", botstates[ent->s.number]->forceinfo);
00282         }
00283 
00284         //rww - parse through the string manually and eat out all the appropriate data
00285         i = 0;
00286 
00287         if (g_forceBasedTeams.integer)
00288         {
00289                 if (ent->client->sess.sessionTeam == TEAM_RED)
00290                 {
00291                         warnClient = !(BG_LegalizedForcePowers(forcePowers, maxRank, HasSetSaberOnly(), FORCE_DARKSIDE, g_gametype.integer, g_forcePowerDisable.integer));
00292                 }
00293                 else if (ent->client->sess.sessionTeam == TEAM_BLUE)
00294                 {
00295                         warnClient = !(BG_LegalizedForcePowers(forcePowers, maxRank, HasSetSaberOnly(), FORCE_LIGHTSIDE, g_gametype.integer, g_forcePowerDisable.integer));
00296                 }
00297                 else
00298                 {
00299                         warnClient = !(BG_LegalizedForcePowers(forcePowers, maxRank, HasSetSaberOnly(), 0, g_gametype.integer, g_forcePowerDisable.integer));
00300                 }
00301         }
00302         else
00303         {
00304                 warnClient = !(BG_LegalizedForcePowers(forcePowers, maxRank, HasSetSaberOnly(), 0, g_gametype.integer, g_forcePowerDisable.integer));
00305         }
00306 
00307         i_r = 0;
00308         while (forcePowers[i] && forcePowers[i] != '-')
00309         {
00310                 readBuf[i_r] = forcePowers[i];
00311                 i_r++;
00312                 i++;
00313         }
00314         readBuf[i_r] = 0;
00315         //THE RANK
00316         ent->client->ps.fd.forceRank = atoi(readBuf);
00317         i++;
00318 
00319         i_r = 0;
00320         while (forcePowers[i] && forcePowers[i] != '-')
00321         {
00322                 readBuf[i_r] = forcePowers[i];
00323                 i_r++;
00324                 i++;
00325         }
00326         readBuf[i_r] = 0;
00327         //THE SIDE
00328         ent->client->ps.fd.forceSide = atoi(readBuf);
00329         i++;
00330 
00331 
00332         if ( g_gametype.integer != GT_SIEGE && (ent->r.svFlags & SVF_BOT) && botstates[ent->s.number] )
00333         { //hmm..I'm going to cheat here.
00334                 int oldI = i;
00335                 i_r = 0;
00336                 while (forcePowers[i] && forcePowers[i] != '\n' &&
00337                         i_r < NUM_FORCE_POWERS)
00338                 {
00339                         if (ent->client->ps.fd.forceSide == FORCE_LIGHTSIDE)
00340                         {
00341                                 if (i_r == FP_ABSORB)
00342                                 {
00343                                         forcePowers[i] = '3';
00344                                 }
00345                                 if (botstates[ent->s.number]->settings.skill >= 4)
00346                                 { //cheat and give them more stuff
00347                                         if (i_r == FP_HEAL)
00348                                         {
00349                                                 forcePowers[i] = '3';
00350                                         }
00351                                         else if (i_r == FP_PROTECT)
00352                                         {
00353                                                 forcePowers[i] = '3';
00354                                         }
00355                                 }
00356                         }
00357                         else if (ent->client->ps.fd.forceSide == FORCE_DARKSIDE)
00358                         {
00359                                 if (botstates[ent->s.number]->settings.skill >= 4)
00360                                 {
00361                                         if (i_r == FP_GRIP)
00362                                         {
00363                                                 forcePowers[i] = '3';
00364                                         }
00365                                         else if (i_r == FP_LIGHTNING)
00366                                         {
00367                                                 forcePowers[i] = '3';
00368                                         }
00369                                         else if (i_r == FP_RAGE)
00370                                         {
00371                                                 forcePowers[i] = '3';
00372                                         }
00373                                         else if (i_r == FP_DRAIN)
00374                                         {
00375                                                 forcePowers[i] = '3';
00376                                         }
00377                                 }
00378                         }
00379 
00380                         if (i_r == FP_PUSH)
00381                         {
00382                                 forcePowers[i] = '3';
00383                         }
00384                         else if (i_r == FP_PULL)
00385                         {
00386                                 forcePowers[i] = '3';
00387                         }
00388 
00389                         i++;
00390                         i_r++;
00391                 }
00392                 i = oldI;
00393         }
00394 
00395         i_r = 0;
00396         while (forcePowers[i] && forcePowers[i] != '\n' &&
00397                 i_r < NUM_FORCE_POWERS)
00398         {
00399                 readBuf[0] = forcePowers[i];
00400                 readBuf[1] = 0;
00401 
00402                 ent->client->ps.fd.forcePowerLevel[i_r] = atoi(readBuf);
00403                 if (ent->client->ps.fd.forcePowerLevel[i_r])
00404                 {
00405                         ent->client->ps.fd.forcePowersKnown |= (1 << i_r);
00406                 }
00407                 else
00408                 {
00409                         ent->client->ps.fd.forcePowersKnown &= ~(1 << i_r);
00410                 }
00411                 i++;
00412                 i_r++;
00413         }
00414         //THE POWERS
00415 
00416         if (ent->s.eType != ET_NPC)
00417         {
00418                 if (HasSetSaberOnly())
00419                 {
00420                         gentity_t *te = G_TempEntity( vec3_origin, EV_SET_FREE_SABER );
00421                         te->r.svFlags |= SVF_BROADCAST;
00422                         te->s.eventParm = 1;
00423                 }
00424                 else
00425                 {
00426                         gentity_t *te = G_TempEntity( vec3_origin, EV_SET_FREE_SABER );
00427                         te->r.svFlags |= SVF_BROADCAST;
00428                         te->s.eventParm = 0;
00429                 }
00430 
00431                 if (g_forcePowerDisable.integer)
00432                 {
00433                         gentity_t *te = G_TempEntity( vec3_origin, EV_SET_FORCE_DISABLE );
00434                         te->r.svFlags |= SVF_BROADCAST;
00435                         te->s.eventParm = 1;
00436                 }
00437                 else
00438                 {
00439                         gentity_t *te = G_TempEntity( vec3_origin, EV_SET_FORCE_DISABLE );
00440                         te->r.svFlags |= SVF_BROADCAST;
00441                         te->s.eventParm = 0;
00442                 }
00443         }
00444 
00445         //rww - It seems we currently want to always do this, even if the player isn't exceeding the max
00446         //rank, so..
00447 //      if (g_gametype.integer == GT_DUEL || g_gametype.integer == GT_POWERDUEL)
00448 //      { //totally messes duel up to force someone into spec mode, and besides, each "round" is
00449           //counted as a full restart
00450 //              ent->client->sess.setForce = qtrue;
00451 //      }
00452 
00453         if (ent->s.eType == ET_NPC)
00454         {
00455                 ent->client->sess.setForce = qtrue;
00456         }
00457         else if (g_gametype.integer == GT_SIEGE)
00458         {
00459                 if (!ent->client->sess.setForce)
00460                 {
00461                         ent->client->sess.setForce = qtrue;
00462                         //bring up the class selection menu
00463                         trap_SendServerCommand(ent-g_entities, "scl");
00464                 }
00465         }
00466         else
00467         {
00468                 if (warnClient || !ent->client->sess.setForce)
00469                 { //the client's rank is too high for the server and has been autocapped, so tell them
00470                         if (g_gametype.integer != GT_HOLOCRON && g_gametype.integer != GT_JEDIMASTER )
00471                         {
00472 #ifdef EVENT_FORCE_RANK
00473                                 gentity_t *te = G_TempEntity( vec3_origin, EV_GIVE_NEW_RANK );
00474 
00475                                 te->r.svFlags |= SVF_BROADCAST;
00476                                 te->s.trickedentindex = ent->s.number;
00477                                 te->s.eventParm = maxRank;
00478                                 te->s.bolt1 = 0;
00479 #endif
00480                                 didEvent = qtrue;
00481 
00482 //                              if (!(ent->r.svFlags & SVF_BOT) && g_gametype.integer != GT_DUEL && g_gametype.integer != GT_POWERDUEL && ent->s.eType != ET_NPC)
00483                                 if (!(ent->r.svFlags & SVF_BOT) && ent->s.eType != ET_NPC)
00484                                 {
00485                                         if (!g_teamAutoJoin.integer)
00486                                         {
00487                                                 //Make them a spectator so they can set their powerups up without being bothered.
00488                                                 ent->client->sess.sessionTeam = TEAM_SPECTATOR;
00489                                                 ent->client->sess.spectatorState = SPECTATOR_FREE;
00490                                                 ent->client->sess.spectatorClient = 0;
00491 
00492                                                 ent->client->pers.teamState.state = TEAM_BEGIN;
00493                                                 trap_SendServerCommand(ent-g_entities, "spc");  // Fire up the profile menu
00494                                         }
00495                                 }
00496 
00497 #ifdef EVENT_FORCE_RANK
00498                                 te->s.bolt2 = ent->client->sess.sessionTeam;
00499 #else
00500                                 //Event isn't very reliable, I made it a string. This way I can send it to just one
00501                                 //client also, as opposed to making a broadcast event.
00502                                 trap_SendServerCommand(ent->s.number, va("nfr %i %i %i", maxRank, 1, ent->client->sess.sessionTeam));
00503                                 //Arg1 is new max rank, arg2 is non-0 if force menu should be shown, arg3 is the current team
00504 #endif
00505                         }
00506                         ent->client->sess.setForce = qtrue;
00507                 }
00508 
00509                 if (!didEvent )
00510                 {
00511 #ifdef EVENT_FORCE_RANK
00512                         gentity_t *te = G_TempEntity( vec3_origin, EV_GIVE_NEW_RANK );
00513 
00514                         te->r.svFlags |= SVF_BROADCAST;
00515                         te->s.trickedentindex = ent->s.number;
00516                         te->s.eventParm = maxRank;
00517                         te->s.bolt1 = 1;
00518                         te->s.bolt2 = ent->client->sess.sessionTeam;
00519 #else
00520                         trap_SendServerCommand(ent->s.number, va("nfr %i %i %i", maxRank, 0, ent->client->sess.sessionTeam));
00521 #endif
00522                 }
00523 
00524                 if (warnClientLimit)
00525                 { //the server has one or more force powers disabled and the client is using them in his config
00526                         //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\""));
00527                 }
00528         }
00529 
00530         i = 0;
00531         while (i < NUM_FORCE_POWERS)
00532         {
00533                 if ((ent->client->ps.fd.forcePowersKnown & (1 << i)) &&
00534                         !ent->client->ps.fd.forcePowerLevel[i])
00535                 { //err..
00536                         ent->client->ps.fd.forcePowersKnown &= ~(1 << i);
00537                 }
00538                 else
00539                 {
00540                         if (i != FP_LEVITATION && i != FP_SABER_OFFENSE && i != FP_SABER_DEFENSE && i != FP_SABERTHROW)
00541                         {
00542                                 lastFPKnown = i;
00543                         }
00544                 }
00545 
00546                 i++;
00547         }
00548 
00549         if (ent->client->ps.fd.forcePowersKnown & ent->client->sess.selectedFP)
00550         {
00551                 ent->client->ps.fd.forcePowerSelected = ent->client->sess.selectedFP;
00552         }
00553 
00554         if (!(ent->client->ps.fd.forcePowersKnown & (1 << ent->client->ps.fd.forcePowerSelected)))
00555         {
00556                 if (lastFPKnown != -1)
00557                 {
00558                         ent->client->ps.fd.forcePowerSelected = lastFPKnown;
00559                 }
00560                 else
00561                 {
00562                         ent->client->ps.fd.forcePowerSelected = 0;
00563                 }
00564         }
00565 
00566         while (i < NUM_FORCE_POWERS)
00567         {
00568                 ent->client->ps.fd.forcePowerBaseLevel[i] = ent->client->ps.fd.forcePowerLevel[i];
00569                 i++;
00570         }
00571         ent->client->ps.fd.forceUsingAdded = 0;
00572 }
00573 
00574 void WP_SpawnInitForcePowers( gentity_t *ent )
00575 {
00576         int i = 0;
00577 
00578         ent->client->ps.saberAttackChainCount = 0;
00579 
00580         i = 0;
00581 
00582         while (i < NUM_FORCE_POWERS)
00583         {
00584                 if (ent->client->ps.fd.forcePowersActive & (1 << i))
00585                 {
00586                         WP_ForcePowerStop(ent, i);
00587                 }
00588 
00589                 i++;
00590         }
00591 
00592         ent->client->ps.fd.forceDeactivateAll = 0;
00593 
00594         ent->client->ps.fd.forcePower = ent->client->ps.fd.forcePowerMax = FORCE_POWER_MAX;
00595         ent->client->ps.fd.forcePowerRegenDebounceTime = 0;
00596         ent->client->ps.fd.forceGripEntityNum = ENTITYNUM_NONE;
00597         ent->client->ps.fd.forceMindtrickTargetIndex = 0;
00598         ent->client->ps.fd.forceMindtrickTargetIndex2 = 0;
00599         ent->client->ps.fd.forceMindtrickTargetIndex3 = 0;
00600         ent->client->ps.fd.forceMindtrickTargetIndex4 = 0;
00601 
00602         ent->client->ps.holocronBits = 0;
00603 
00604         i = 0;
00605         while (i < NUM_FORCE_POWERS)
00606         {
00607                 ent->client->ps.holocronsCarried[i] = 0;
00608                 i++;
00609         }
00610 
00611         if (g_gametype.integer == GT_HOLOCRON)
00612         {
00613                 i = 0;
00614                 while (i < NUM_FORCE_POWERS)
00615                 {
00616                         ent->client->ps.fd.forcePowerLevel[i] = FORCE_LEVEL_0;
00617                         i++;
00618                 }
00619 
00620                 if (HasSetSaberOnly())
00621                 {
00622                         if (ent->client->ps.fd.forcePowerLevel[FP_SABER_OFFENSE] < FORCE_LEVEL_1)
00623                         {
00624                                 ent->client->ps.fd.forcePowerLevel[FP_SABER_OFFENSE] = FORCE_LEVEL_1;
00625                         }
00626                         if (ent->client->ps.fd.forcePowerLevel[FP_SABER_DEFENSE] < FORCE_LEVEL_1)
00627                         {
00628                                 ent->client->ps.fd.forcePowerLevel[FP_SABER_DEFENSE] = FORCE_LEVEL_1;
00629                         }
00630                 }
00631         }
00632 
00633         i = 0;
00634 
00635         while (i < NUM_FORCE_POWERS)
00636         {
00637                 ent->client->ps.fd.forcePowerDebounce[i] = 0;
00638                 ent->client->ps.fd.forcePowerDuration[i] = 0;
00639 
00640                 i++;
00641         }
00642 
00643         ent->client->ps.fd.forcePowerRegenDebounceTime = 0;
00644         ent->client->ps.fd.forceJumpZStart = 0;
00645         ent->client->ps.fd.forceJumpCharge = 0;
00646         ent->client->ps.fd.forceJumpSound = 0;
00647         ent->client->ps.fd.forceGripDamageDebounceTime = 0;
00648         ent->client->ps.fd.forceGripBeingGripped = 0;
00649         ent->client->ps.fd.forceGripCripple = 0;
00650         ent->client->ps.fd.forceGripUseTime = 0;
00651         ent->client->ps.fd.forceGripSoundTime = 0;
00652         ent->client->ps.fd.forceGripStarted = 0;
00653         ent->client->ps.fd.forceHealTime = 0;
00654         ent->client->ps.fd.forceHealAmount = 0;
00655         ent->client->ps.fd.forceRageRecoveryTime = 0;
00656         ent->client->ps.fd.forceDrainEntNum = ENTITYNUM_NONE;
00657         ent->client->ps.fd.forceDrainTime = 0;
00658 
00659         i = 0;
00660         while (i < NUM_FORCE_POWERS)
00661         {
00662                 if ((ent->client->ps.fd.forcePowersKnown & (1 << i)) &&
00663                         !ent->client->ps.fd.forcePowerLevel[i])
00664                 { //make sure all known powers are cleared if we have level 0 in them
00665                         ent->client->ps.fd.forcePowersKnown &= ~(1 << i);
00666                 }
00667 
00668                 i++;
00669         }
00670 
00671         if (g_gametype.integer == GT_SIEGE &&
00672                 ent->client->siegeClass != -1)
00673         { //Then use the powers for this class.
00674                 i = 0;
00675 
00676                 while (i < NUM_FORCE_POWERS)
00677                 {
00678                         ent->client->ps.fd.forcePowerLevel[i] = bgSiegeClasses[ent->client->siegeClass].forcePowerLevels[i];
00679 
00680                         if (!ent->client->ps.fd.forcePowerLevel[i])
00681                         {
00682                                 ent->client->ps.fd.forcePowersKnown &= ~(1 << i);
00683                         }
00684                         else
00685                         {
00686                                 ent->client->ps.fd.forcePowersKnown |= (1 << i);
00687                         }
00688                         i++;
00689                 }
00690         }
00691 }
00692 
00693 #include "../namespace_begin.h"
00694 extern qboolean BG_InKnockDown( int anim ); //bg_pmove.c
00695 #include "../namespace_end.h"
00696 
00697 int ForcePowerUsableOn(gentity_t *attacker, gentity_t *other, forcePowers_t forcePower)
00698 {
00699         if (other && other->client && BG_HasYsalamiri(g_gametype.integer, &other->client->ps))
00700         {
00701                 return 0;
00702         }
00703 
00704         if (attacker && attacker->client && !BG_CanUseFPNow(g_gametype.integer, &attacker->client->ps, level.time, forcePower))
00705         {
00706                 return 0;
00707         }
00708 
00709         //Dueling fighters cannot use force powers on others, with the exception of force push when locked with each other
00710         if (attacker && attacker->client && attacker->client->ps.duelInProgress)
00711         {
00712                 return 0;
00713         }
00714 
00715         if (other && other->client && other->client->ps.duelInProgress)
00716         {
00717                 return 0;
00718         }
00719 
00720         if (forcePower == FP_GRIP)
00721         {
00722                 if (other && other->client &&
00723                         (other->client->ps.fd.forcePowersActive & (1<<FP_ABSORB)))
00724                 { //don't allow gripping to begin with if they are absorbing
00725                         //play sound indicating that attack was absorbed
00726                         if (other->client->forcePowerSoundDebounce < level.time)
00727                         {
00728                                 gentity_t *abSound = G_PreDefSound(other->client->ps.origin, PDSOUND_ABSORBHIT);
00729                                 abSound->s.trickedentindex = other->s.number;
00730                                 other->client->forcePowerSoundDebounce = level.time + 400;
00731                         }
00732                         return 0;
00733                 }
00734                 else if (other && other->client &&
00735                         other->client->ps.weapon == WP_SABER &&
00736                         BG_SaberInSpecial(other->client->ps.saberMove))
00737                 { //don't grip person while they are in a special or some really bad things can happen.
00738                         return 0;
00739                 }
00740         }
00741 
00742         if (other && other->client &&
00743                 (forcePower == FP_PUSH ||
00744                 forcePower == FP_PULL))
00745         {
00746                 if (BG_InKnockDown(other->client->ps.legsAnim))
00747                 {
00748                         return 0;
00749                 }
00750         }
00751 
00752         if (other && other->client && other->s.eType == ET_NPC &&
00753                 other->s.NPC_class == CLASS_VEHICLE)
00754         { //can't use the force on vehicles.. except lightning
00755                 if (forcePower == FP_LIGHTNING)
00756                 {
00757                         return 1;
00758                 }
00759                 else
00760                 {
00761                         return 0;
00762                 }
00763         }
00764 
00765         if (other && other->client && other->s.eType == ET_NPC &&
00766                 g_gametype.integer == GT_SIEGE)
00767         { //can't use powers at all on npc's normally in siege...
00768                 return 0;
00769         }
00770 
00771         return 1;
00772 }
00773 
00774 qboolean WP_ForcePowerAvailable( gentity_t *self, forcePowers_t forcePower, int overrideAmt )
00775 {
00776         int     drain = overrideAmt ? overrideAmt :
00777                                 forcePowerNeeded[self->client->ps.fd.forcePowerLevel[forcePower]][forcePower];
00778 
00779         if (self->client->ps.fd.forcePowersActive & (1 << forcePower))
00780         { //we're probably going to deactivate it..
00781                 return qtrue;
00782         }
00783         if ( forcePower == FP_LEVITATION )
00784         {
00785                 return qtrue;
00786         }
00787         if ( !drain )
00788         {
00789                 return qtrue;
00790         }
00791         if ((forcePower == FP_DRAIN || forcePower == FP_LIGHTNING) &&
00792                 self->client->ps.fd.forcePower >= 25)
00793         { //it's ok then, drain/lightning are actually duration
00794                 return qtrue;
00795         }
00796         if ( self->client->ps.fd.forcePower < drain )
00797         {
00798                 return qfalse;
00799         }
00800         return qtrue;
00801 }
00802 
00803 qboolean WP_ForcePowerInUse( gentity_t *self, forcePowers_t forcePower )
00804 {
00805         if ( (self->client->ps.fd.forcePowersActive & ( 1 << forcePower )) )
00806         {//already using this power
00807                 return qtrue;
00808         }
00809 
00810         return qfalse;
00811 }
00812 
00813 qboolean WP_ForcePowerUsable( gentity_t *self, forcePowers_t forcePower )
00814 {
00815         if (BG_HasYsalamiri(g_gametype.integer, &self->client->ps))
00816         {
00817                 return qfalse;
00818         }
00819 
00820         if (self->health <= 0 || self->client->ps.stats[STAT_HEALTH] <= 0 ||
00821                 (self->client->ps.eFlags & EF_DEAD))
00822         {
00823                 return qfalse;
00824         }
00825 
00826         if (self->client->ps.pm_flags & PMF_FOLLOW)
00827         { //specs can't use powers through people
00828                 return qfalse;
00829         }
00830         if (self->client->sess.sessionTeam == TEAM_SPECTATOR)
00831         {
00832                 return qfalse;
00833         }
00834         if (self->client->tempSpectate >= level.time)
00835         {
00836                 return qfalse;
00837         }
00838 
00839         if (!BG_CanUseFPNow(g_gametype.integer, &self->client->ps, level.time, forcePower))
00840         {
00841                 return qfalse;
00842         }
00843 
00844         if ( !(self->client->ps.fd.forcePowersKnown & ( 1 << forcePower )) )
00845         {//don't know this power
00846                 return qfalse;
00847         }
00848         
00849         if ( (self->client->ps.fd.forcePowersActive & ( 1 << forcePower )) )
00850         {//already using this power
00851                 if (forcePower != FP_LEVITATION)
00852                 {
00853                         return qfalse;
00854                 }
00855         }
00856 
00857         if (forcePower == FP_LEVITATION && self->client->fjDidJump)
00858         {
00859                 return qfalse;
00860         }
00861 
00862         if (!self->client->ps.fd.forcePowerLevel[forcePower])
00863         {
00864                 return qfalse;
00865         }
00866 
00867         if ( g_debugMelee.integer )
00868         {
00869                 if ( (self->client->ps.pm_flags&PMF_STUCK_TO_WALL) )
00870                 {//no offensive force powers when stuck to wall
00871                         switch ( forcePower )
00872                         {
00873                         case FP_GRIP:
00874                         case FP_LIGHTNING:
00875                         case FP_DRAIN:
00876                         case FP_SABER_OFFENSE:
00877                         case FP_SABER_DEFENSE:
00878                         case FP_SABERTHROW:
00879                                 return qfalse;
00880                                 break;
00881                         }
00882                 }
00883         }
00884 
00885         if ( !self->client->ps.saberHolstered )
00886         {
00887                 if ( (self->client->saber[0].saberFlags&SFL_TWO_HANDED) )
00888                 {
00889                         if ( g_saberRestrictForce.integer )
00890                         {
00891                                 switch ( forcePower )
00892                                 {
00893                                 case FP_PUSH:
00894                                 case FP_PULL:
00895                                 case FP_TELEPATHY:
00896                                 case FP_GRIP:
00897                                 case FP_LIGHTNING:
00898                                 case FP_DRAIN:
00899                                         return qfalse;
00900                                         break;
00901                                 }
00902                         }
00903                 }
00904 
00905                 if ( (self->client->saber[0].saberFlags&SFL_TWO_HANDED)
00906                         || (self->client->saber[0].model && self->client->saber[0].model[0]) )
00907                 {//this saber requires the use of two hands OR our other hand is using an active saber too
00908                         if ( (self->client->saber[0].forceRestrictions&(1<<forcePower)) )
00909                         {//this power is verboten when using this saber
00910                                 return qfalse;
00911                         }
00912                 }
00913 
00914                 if ( self->client->saber[0].model 
00915                         && self->client->saber[0].model[0] )
00916                 {//both sabers on
00917                         if ( g_saberRestrictForce.integer )
00918                         {
00919                                 switch ( forcePower )
00920                                 {
00921                                 case FP_PUSH:
00922                                 case FP_PULL:
00923                                 case FP_TELEPATHY:
00924                                 case FP_GRIP:
00925                                 case FP_LIGHTNING:
00926                                 case FP_DRAIN:
00927                                         return qfalse;
00928                                         break;
00929                                 }
00930                         }
00931                         if ( (self->client->saber[1].forceRestrictions&(1<<forcePower)) )
00932                         {//this power is verboten when using this saber
00933                                 return qfalse;
00934                         }
00935                 }
00936         }
00937         return WP_ForcePowerAvailable( self, forcePower, 0 );   // OVERRIDEFIXME
00938 }
00939 
00940 int WP_AbsorbConversion(gentity_t *attacked, int atdAbsLevel, gentity_t *attacker, int atPower, int atPowerLevel, int atForceSpent)
00941 {
00942         int getLevel = 0;
00943         int addTot = 0;
00944         gentity_t *abSound;
00945 
00946         if (atPower != FP_LIGHTNING &&
00947                 atPower != FP_DRAIN &&
00948                 atPower != FP_GRIP &&
00949                 atPower != FP_PUSH &&
00950                 atPower != FP_PULL)
00951         { //Only these powers can be absorbed
00952                 return -1;
00953         }
00954 
00955         if (!atdAbsLevel)
00956         { //looks like attacker doesn't have any absorb power
00957                 return -1;
00958         }
00959 
00960         if (!(attacked->client->ps.fd.forcePowersActive & (1 << FP_ABSORB)))
00961         { //absorb is not active
00962                 return -1;
00963         }
00964 
00965         //Subtract absorb power level from the offensive force power
00966         getLevel = atPowerLevel;
00967         getLevel -= atdAbsLevel;
00968 
00969         if (getLevel < 0)
00970         {
00971                 getLevel = 0;
00972         }
00973 
00974         //let the attacker absorb an amount of force used in this attack based on his level of absorb
00975         addTot = (atForceSpent/3)*attacked->client->ps.fd.forcePowerLevel[FP_ABSORB];
00976 
00977         if (addTot < 1 && atForceSpent >= 1)
00978         {
00979                 addTot = 1;
00980         }
00981         attacked->client->ps.fd.forcePower += addTot;
00982         if (attacked->client->ps.fd.forcePower > 100)
00983         {
00984                 attacked->client->ps.fd.forcePower = 100;
00985         }
00986 
00987         //play sound indicating that attack was absorbed
00988         if (attacked->client->forcePowerSoundDebounce < level.time)
00989         {
00990                 abSound = G_PreDefSound(attacked->client->ps.origin, PDSOUND_ABSORBHIT);
00991                 abSound->s.trickedentindex = attacked->s.number;
00992 
00993                 attacked->client->forcePowerSoundDebounce = level.time + 400;
00994         }
00995 
00996         return getLevel;
00997 }
00998 
00999 void WP_ForcePowerRegenerate( gentity_t *self, int overrideAmt )
01000 { //called on a regular interval to regenerate force power.
01001         if ( !self->client )
01002         {
01003                 return;
01004         }
01005 
01006         if ( overrideAmt )
01007         { //custom regen amount
01008                 self->client->ps.fd.forcePower += overrideAmt;
01009         }
01010         else
01011         { //otherwise, just 1
01012                 self->client->ps.fd.forcePower++;
01013         }
01014 
01015         if ( self->client->ps.fd.forcePower > self->client->ps.fd.forcePowerMax )
01016         { //cap it off at the max (default 100)
01017                 self->client->ps.fd.forcePower = self->client->ps.fd.forcePowerMax;
01018         }
01019 }
01020 
01021 void WP_ForcePowerStart( gentity_t *self, forcePowers_t forcePower, int overrideAmt )
01022 { //activate the given force power
01023         int     duration = 0;
01024         qboolean hearable = qfalse;
01025         float hearDist = 0;
01026 
01027         if (!WP_ForcePowerAvailable( self, forcePower, overrideAmt ))
01028         {
01029                 return;
01030         }
01031 
01032         if ( BG_FullBodyTauntAnim( self->client->ps.legsAnim ) )
01033         {//stop taunt
01034                 self->client->ps.legsTimer = 0;
01035         }
01036         if ( BG_FullBodyTauntAnim( self->client->ps.torsoAnim ) )
01037         {//stop taunt
01038                 self->client->ps.torsoTimer = 0;
01039         }
01040         //hearable and hearDist are merely for the benefit of bots, and not related to if a sound is actually played.
01041         //If duration is set, the force power will assume to be timer-based.
01042         switch( (int)forcePower )
01043         {
01044         case FP_HEAL:
01045                 hearable = qtrue;
01046                 hearDist = 256;
01047                 self->client->ps.fd.forcePowersActive |= ( 1 << forcePower );
01048                 break;
01049         case FP_LEVITATION:
01050                 hearable = qtrue;
01051                 hearDist = 256;
01052                 self->client->ps.fd.forcePowersActive |= ( 1 << forcePower );
01053                 break;
01054         case FP_SPEED:
01055                 hearable = qtrue;
01056                 hearDist = 256;
01057                 if (self->client->ps.fd.forcePowerLevel[FP_SPEED] == FORCE_LEVEL_1)
01058                 {
01059                         duration = 10000;
01060                 }
01061                 else if (self->client->ps.fd.forcePowerLevel[FP_SPEED] == FORCE_LEVEL_2)
01062                 {
01063                         duration = 15000;
01064                 }
01065                 else if (self->client->ps.fd.forcePowerLevel[FP_SPEED] == FORCE_LEVEL_3)
01066                 {
01067                         duration = 20000;
01068                 }
01069                 else //shouldn't get here
01070                 {
01071                         break;
01072                 }
01073 
01074                 if (overrideAmt)
01075                 {
01076                         duration = overrideAmt;
01077                 }
01078 
01079                 self->client->ps.fd.forcePowersActive |= ( 1 << forcePower );
01080                 break;
01081         case FP_PUSH:
01082                 hearable = qtrue;
01083                 hearDist = 256;
01084                 break;
01085         case FP_PULL:
01086                 hearable = qtrue;
01087                 hearDist = 256;
01088                 break;
01089         case FP_TELEPATHY:
01090                 hearable = qtrue;
01091                 hearDist = 256;
01092                 if (self->client->ps.fd.forcePowerLevel[FP_TELEPATHY] == FORCE_LEVEL_1)
01093                 {
01094                         duration = 20000;
01095                 }
01096                 else if (self->client->ps.fd.forcePowerLevel[FP_TELEPATHY] == FORCE_LEVEL_2)
01097                 {
01098                         duration = 25000;
01099                 }
01100                 else if (self->client->ps.fd.forcePowerLevel[FP_TELEPATHY] == FORCE_LEVEL_3)
01101                 {
01102                         duration = 30000;
01103                 }
01104                 else //shouldn't get here
01105                 {
01106                         break;
01107                 }
01108 
01109                 self->client->ps.fd.forcePowersActive |= ( 1 << forcePower );
01110                 break;
01111         case FP_GRIP:
01112                 hearable = qtrue;
01113                 hearDist = 256;
01114                 self->client->ps.fd.forcePowersActive |= ( 1 << forcePower );
01115                 self->client->ps.powerups[PW_DISINT_4] = level.time + 60000;
01116                 break;
01117         case FP_LIGHTNING:
01118                 hearable = qtrue;
01119                 hearDist = 512;
01120                 duration = overrideAmt;
01121                 overrideAmt = 0;
01122                 self->client->ps.fd.forcePowersActive |= ( 1 << forcePower );
01123                 self->client->ps.activeForcePass = self->client->ps.fd.forcePowerLevel[FP_LIGHTNING];
01124                 break;
01125         case FP_RAGE:
01126                 hearable = qtrue;
01127                 hearDist = 256;
01128                 if (self->client->ps.fd.forcePowerLevel[FP_RAGE] == FORCE_LEVEL_1)
01129                 {
01130                         duration = 8000;
01131                 }
01132                 else if (self->client->ps.fd.forcePowerLevel[FP_RAGE] == FORCE_LEVEL_2)
01133                 {
01134                         duration = 14000;
01135                 }
01136                 else if (self->client->ps.fd.forcePowerLevel[FP_RAGE] == FORCE_LEVEL_3)
01137                 {
01138                         duration = 20000;
01139                 }
01140                 else //shouldn't get here
01141                 {
01142                         break;
01143                 }
01144 
01145                 self->client->ps.fd.forcePowersActive |= ( 1 << forcePower );
01146                 break;
01147         case FP_PROTECT:
01148                 hearable = qtrue;
01149                 hearDist = 256;
01150                 duration = 20000;
01151                 self->client->ps.fd.forcePowersActive |= ( 1 << forcePower );
01152                 break;
01153         case FP_ABSORB:
01154                 hearable = qtrue;
01155                 hearDist = 256;
01156                 duration = 20000;
01157                 self->client->ps.fd.forcePowersActive |= ( 1 << forcePower );
01158                 break;
01159         case FP_TEAM_HEAL:
01160                 hearable = qtrue;
01161                 hearDist = 256;
01162                 self->client->ps.fd.forcePowersActive |= ( 1 << forcePower );
01163                 break;
01164         case FP_TEAM_FORCE:
01165                 hearable = qtrue;
01166                 hearDist = 256;
01167                 self->client->ps.fd.forcePowersActive |= ( 1 << forcePower );
01168                 break;
01169         case FP_DRAIN:
01170                 hearable = qtrue;
01171                 hearDist = 256;
01172                 duration = overrideAmt;
01173                 overrideAmt = 0;
01174                 self->client->ps.fd.forcePowersActive |= ( 1 << forcePower );
01175                 //self->client->ps.activeForcePass = self->client->ps.fd.forcePowerLevel[FP_DRAIN];
01176                 break;
01177         case FP_SEE:
01178                 hearable = qtrue;
01179                 hearDist = 256;
01180                 if (self->client->ps.fd.forcePowerLevel[FP_SEE] == FORCE_LEVEL_1)
01181                 {
01182                         duration = 10000;
01183                 }
01184                 else if (self->client->ps.fd.forcePowerLevel[FP_SEE] == FORCE_LEVEL_2)
01185                 {
01186                         duration = 20000;
01187                 }
01188                 else if (self->client->ps.fd.forcePowerLevel[FP_SEE] == FORCE_LEVEL_3)
01189                 {
01190                         duration = 30000;
01191                 }
01192                 else //shouldn't get here
01193                 {
01194                         break;
01195                 }
01196 
01197                 self->client->ps.fd.forcePowersActive |= ( 1 << forcePower );
01198                 break;
01199         case FP_SABER_OFFENSE:
01200                 break;
01201         case FP_SABER_DEFENSE:
01202                 break;
01203         case FP_SABERTHROW:
01204                 break;
01205         default:
01206                 break;
01207         }
01208 
01209         if ( duration )
01210         {
01211                 self->client->ps.fd.forcePowerDuration[forcePower] = level.time + duration;
01212         }
01213         else
01214         {
01215                 self->client->ps.fd.forcePowerDuration[forcePower] = 0;
01216         }
01217 
01218         if (hearable)
01219         {
01220                 self->client->ps.otherSoundLen = hearDist;
01221                 self->client->ps.otherSoundTime = level.time + 100;
01222         }
01223         
01224         self->client->ps.fd.forcePowerDebounce[forcePower] = 0;
01225 
01226         if ((int)forcePower == FP_SPEED && overrideAmt)
01227         {
01228                 BG_ForcePowerDrain( &self->client->ps, forcePower, overrideAmt*0.025 );
01229         }
01230         else if ((int)forcePower != FP_GRIP && (int)forcePower != FP_DRAIN)
01231         { //grip and drain drain as damage is done
01232                 BG_ForcePowerDrain( &self->client->ps, forcePower, overrideAmt );
01233         }
01234 }
01235 
01236 void ForceHeal( gentity_t *self )
01237 {
01238         if ( self->health <= 0 )
01239         {
01240                 return;
01241         }
01242 
01243         if ( !WP_ForcePowerUsable( self, FP_HEAL ) )
01244         {
01245                 return;
01246         }
01247 
01248         if ( self->health >= self->client->ps.stats[STAT_MAX_HEALTH])
01249         {
01250                 return;
01251         }
01252 
01253         if (self->client->ps.fd.forcePowerLevel[FP_HEAL] == FORCE_LEVEL_3)
01254         {
01255                 self->health += 25; //This was 50, but that angered the Balance God.
01256                 
01257                 if (self->health > self->client->ps.stats[STAT_MAX_HEALTH])
01258                 {
01259                         self->health = self->client->ps.stats[STAT_MAX_HEALTH];
01260                 }
01261                 BG_ForcePowerDrain( &self->client->ps, FP_HEAL, 0 );
01262         }
01263         else if (self->client->ps.fd.forcePowerLevel[FP_HEAL] == FORCE_LEVEL_2)
01264         {
01265                 self->health += 10;
01266                 
01267                 if (self->health > self->client->ps.stats[STAT_MAX_HEALTH])
01268                 {
01269                         self->health = self->client->ps.stats[STAT_MAX_HEALTH];
01270                 }
01271                 BG_ForcePowerDrain( &self->client->ps, FP_HEAL, 0 );
01272         }
01273         else
01274         {
01275                 self->health += 5;
01276                 
01277                 if (self->health > self->client->ps.stats[STAT_MAX_HEALTH])
01278                 {
01279                         self->health = self->client->ps.stats[STAT_MAX_HEALTH];
01280                 }
01281                 BG_ForcePowerDrain( &self->client->ps, FP_HEAL, 0 );
01282         }
01283         /*
01284         else
01285         {
01286                 WP_ForcePowerStart( self, FP_HEAL, 0 );
01287         }
01288         */
01289         //NOTE: Decided to make all levels instant.
01290 
01291         G_Sound( self, CHAN_ITEM, G_SoundIndex("sound/weapons/force/heal.wav") );
01292 }
01293 
01294 void WP_AddToClientBitflags(gentity_t *ent, int entNum)
01295 {
01296         if (!ent)
01297         {
01298                 return;
01299         }
01300 
01301         if (entNum > 47)
01302         {
01303                 ent->s.trickedentindex4 |= (1 << (entNum-48));
01304         }
01305         else if (entNum > 31)
01306         {
01307                 ent->s.trickedentindex3 |= (1 << (entNum-32));
01308         }
01309         else if (entNum > 15)
01310         {
01311                 ent->s.trickedentindex2 |= (1 << (entNum-16));
01312         }
01313         else
01314         {
01315                 ent->s.trickedentindex |= (1 << entNum);
01316         }
01317 }
01318 
01319 void ForceTeamHeal( gentity_t *self )
01320 {
01321         float radius = 256;
01322         int i = 0;
01323         gentity_t *ent;
01324         vec3_t a;
01325         int numpl = 0;
01326         int pl[MAX_CLIENTS];
01327         int healthadd = 0;
01328         gentity_t *te = NULL;
01329 
01330         if ( self->health <= 0 )
01331         {
01332                 return;
01333         }
01334 
01335         if ( !WP_ForcePowerUsable( self, FP_TEAM_HEAL ) )
01336         {
01337                 return;
01338         }
01339 
01340         if (self->client->ps.fd.forcePowerDebounce[FP_TEAM_HEAL] >= level.time)
01341         {
01342                 return;
01343         }
01344 
01345         if (self->client->ps.fd.forcePowerLevel[FP_TEAM_HEAL] == FORCE_LEVEL_2)
01346         {
01347                 radius *= 1.5;
01348         }
01349         if (self->client->ps.fd.forcePowerLevel[FP_TEAM_HEAL] == FORCE_LEVEL_3)
01350         {
01351                 radius *= 2;
01352         }
01353 
01354         while (i < MAX_CLIENTS)
01355         {
01356                 ent = &g_entities[i];
01357 
01358                 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) &&
01359                         trap_InPVS(self->client->ps.origin, ent->client->ps.origin))
01360                 {
01361                         VectorSubtract(self->client->ps.origin, ent->client->ps.origin, a);
01362 
01363                         if (VectorLength(a) <= radius)
01364                         {
01365                                 pl[numpl] = i;
01366                                 numpl++;
01367                         }
01368                 }
01369 
01370                 i++;
01371         }
01372 
01373         if (numpl < 1)
01374         {
01375                 return;
01376         }
01377 
01378         if (numpl == 1)
01379         {
01380                 healthadd = 50;
01381         }
01382         else if (numpl == 2)
01383         {
01384                 healthadd = 33;
01385         }
01386         else
01387         {
01388                 healthadd = 25;
01389         }
01390 
01391         self->client->ps.fd.forcePowerDebounce[FP_TEAM_HEAL] = level.time + 2000;
01392         i = 0;
01393 
01394         while (i < numpl)
01395         {
01396                 if (g_entities[pl[i]].client->ps.stats[STAT_HEALTH] > 0 &&
01397                         g_entities[pl[i]].health > 0)
01398                 {
01399                         g_entities[pl[i]].client->ps.stats[STAT_HEALTH] += healthadd;
01400                         if (g_entities[pl[i]].client->ps.stats[STAT_HEALTH] > g_entities[pl[i]].client->ps.stats[STAT_MAX_HEALTH])
01401                         {
01402                                 g_entities[pl[i]].client->ps.stats[STAT_HEALTH] = g_entities[pl[i]].client->ps.stats[STAT_MAX_HEALTH];
01403                         }
01404 
01405                         g_entities[pl[i]].health = g_entities[pl[i]].client->ps.stats[STAT_HEALTH];
01406 
01407                         //At this point we know we got one, so add him into the collective event client bitflag
01408                         if (!te)
01409                         {
01410                                 te = G_TempEntity( self->client->ps.origin, EV_TEAM_POWER);
01411                                 te->s.eventParm = 1; //eventParm 1 is heal, eventParm 2 is force regen
01412 
01413                                 //since we had an extra check above, do the drain now because we got at least one guy
01414                                 BG_ForcePowerDrain( &self->client->ps, FP_TEAM_HEAL, forcePowerNeeded[self->client->ps.fd.forcePowerLevel[FP_TEAM_HEAL]][FP_TEAM_HEAL] );
01415                         }
01416 
01417                         WP_AddToClientBitflags(te, pl[i]);
01418                         //Now cramming it all into one event.. doing this many g_sound events at once was a Bad Thing.
01419                 }
01420                 i++;
01421         }
01422 }
01423 
01424 void ForceTeamForceReplenish( gentity_t *self )
01425 {
01426         float radius = 256;
01427         int i = 0;
01428         gentity_t *ent;
01429         vec3_t a;
01430         int numpl = 0;
01431         int pl[MAX_CLIENTS];
01432         int poweradd = 0;
01433         gentity_t *te = NULL;
01434 
01435         if ( self->health <= 0 )
01436         {
01437                 return;
01438         }
01439 
01440         if ( !WP_ForcePowerUsable( self, FP_TEAM_FORCE ) )
01441         {
01442                 return;
01443         }
01444 
01445         if (self->client->ps.fd.forcePowerDebounce[FP_TEAM_FORCE] >= level.time)
01446         {
01447                 return;
01448         }
01449 
01450         if (self->client->ps.fd.forcePowerLevel[FP_TEAM_FORCE] == FORCE_LEVEL_2)
01451         {
01452                 radius *= 1.5;
01453         }
01454         if (self->client->ps.fd.forcePowerLevel[FP_TEAM_FORCE] == FORCE_LEVEL_3)
01455         {
01456                 radius *= 2;
01457         }
01458 
01459         while (i < MAX_CLIENTS)
01460         {
01461                 ent = &g_entities[i];
01462 
01463                 if (ent && ent->client && self != ent && OnSameTeam(self, ent) && ent->client->ps.fd.forcePower < 100 && ForcePowerUsableOn(self, ent, FP_TEAM_FORCE) &&
01464                         trap_InPVS(self->client->ps.origin, ent->client->ps.origin))
01465                 {
01466                         VectorSubtract(self->client->ps.origin, ent->client->ps.origin, a);
01467 
01468                         if (VectorLength(a) <= radius)
01469                         {
01470                                 pl[numpl] = i;
01471                                 numpl++;
01472                         }
01473                 }
01474 
01475                 i++;
01476         }
01477 
01478         if (numpl < 1)
01479         {
01480                 return;
01481         }
01482 
01483         if (numpl == 1)
01484         {
01485                 poweradd = 50;
01486         }
01487         else if (numpl == 2)
01488         {
01489                 poweradd = 33;
01490         }
01491         else
01492         {
01493                 poweradd = 25;
01494         }
01495         self->client->ps.fd.forcePowerDebounce[FP_TEAM_FORCE] = level.time + 2000;
01496 
01497         BG_ForcePowerDrain( &self->client->ps, FP_TEAM_FORCE, forcePowerNeeded[self->client->ps.fd.forcePowerLevel[FP_TEAM_FORCE]][FP_TEAM_FORCE] );
01498 
01499         i = 0;
01500 
01501         while (i < numpl)
01502         {
01503                 g_entities[pl[i]].client->ps.fd.forcePower += poweradd;
01504                 if (g_entities[pl[i]].client->ps.fd.forcePower > 100)
01505                 {
01506                         g_entities[pl[i]].client->ps.fd.forcePower = 100;
01507                 }
01508 
01509                 //At this point we know we got one, so add him into the collective event client bitflag
01510                 if (!te)
01511                 {
01512                         te = G_TempEntity( self->client->ps.origin, EV_TEAM_POWER);
01513                         te->s.eventParm = 2; //eventParm 1 is heal, eventParm 2 is force regen
01514                 }
01515 
01516                 WP_AddToClientBitflags(te, pl[i]);
01517                 //Now cramming it all into one event.. doing this many g_sound events at once was a Bad Thing.
01518                 
01519                 i++;
01520         }
01521 }
01522 
01523 void ForceGrip( gentity_t *self )
01524 {
01525         trace_t tr;
01526         vec3_t tfrom, tto, fwd;
01527 
01528         if ( self->health <= 0 )
01529         {
01530                 return;
01531         }
01532 
01533         if (self->client->ps.forceHandExtend != HANDEXTEND_NONE)
01534         {
01535                 return;
01536         }
01537 
01538         if (self->client->ps.weaponTime > 0)
01539         {
01540                 return;
01541         }
01542 
01543         if (self->client->ps.fd.forceGripUseTime > level.time)
01544         {
01545                 return;
01546         }
01547 
01548         if ( !WP_ForcePowerUsable( self, FP_GRIP ) )
01549         {
01550                 return;
01551         }
01552 
01553         VectorCopy(self->client->ps.origin, tfrom);
01554         tfrom[2] += self->client->ps.viewheight;
01555         AngleVectors(self->client->ps.viewangles, fwd, NULL, NULL);
01556         tto[0] = tfrom[0] + fwd[0]*MAX_GRIP_DISTANCE;
01557         tto[1] = tfrom[1] + fwd[1]*MAX_GRIP_DISTANCE;
01558         tto[2] = tfrom[2] + fwd[2]*MAX_GRIP_DISTANCE;
01559 
01560         trap_Trace(&tr, tfrom, NULL, NULL, tto, self->s.number, MASK_PLAYERSOLID);
01561 
01562         if ( tr.fraction != 1.0 &&
01563                 tr.entityNum != ENTITYNUM_NONE &&
01564                 g_entities[tr.entityNum].client &&
01565                 !g_entities[tr.entityNum].client->ps.fd.forceGripCripple &&
01566                 g_entities[tr.entityNum].client->ps.fd.forceGripBeingGripped < level.time &&
01567                 ForcePowerUsableOn(self, &g_entities[tr.entityNum], FP_GRIP) &&
01568                 (g_friendlyFire.integer || !OnSameTeam(self, &g_entities[tr.entityNum])) ) //don't grip someone who's still crippled
01569         {
01570                 if (g_entities[tr.entityNum].s.number < MAX_CLIENTS && g_entities[tr.entityNum].client->ps.m_iVehicleNum)
01571                 { //a player on a vehicle
01572                         gentity_t *vehEnt = &g_entities[g_entities[tr.entityNum].client->ps.m_iVehicleNum];
01573                         if (vehEnt->inuse && vehEnt->client && vehEnt->m_pVehicle)
01574                         {
01575                                 if (vehEnt->m_pVehicle->m_pVehicleInfo->type == VH_SPEEDER ||
01576                                         vehEnt->m_pVehicle->m_pVehicleInfo->type == VH_ANIMAL)
01577                                 { //push the guy off
01578                                         vehEnt->m_pVehicle->m_pVehicleInfo->Eject(vehEnt->m_pVehicle, (bgEntity_t *)&g_entities[tr.entityNum], qfalse);
01579                                 }
01580                         }
01581                 }
01582                 self->client->ps.fd.forceGripEntityNum = tr.entityNum;
01583                 g_entities[tr.entityNum].client->ps.fd.forceGripStarted = level.time;
01584                 self->client->ps.fd.forceGripDamageDebounceTime = 0;
01585 
01586                 self->client->ps.forceHandExtend = HANDEXTEND_FORCE_HOLD;
01587                 self->client->ps.forceHandExtendTime = level.time + 5000;
01588         }
01589         else
01590         {
01591                 self->client->ps.fd.forceGripEntityNum = ENTITYNUM_NONE;
01592                 return;
01593         }
01594 }
01595 
01596 void ForceSpeed( gentity_t *self, int forceDuration )
01597 {
01598         if ( self->health <= 0 )
01599         {
01600                 return;
01601         }
01602 
01603         if (self->client->ps.forceAllowDeactivateTime < level.time &&
01604                 (self->client->ps.fd.forcePowersActive & (1 << FP_SPEED)) )
01605         {
01606                 WP_ForcePowerStop( self, FP_SPEED );
01607                 return;
01608         }
01609 
01610         if ( !WP_ForcePowerUsable( self, FP_SPEED ) )
01611         {
01612                 return;
01613         }
01614 
01615         if ( self->client->holdingObjectiveItem >= MAX_CLIENTS  
01616                 && self->client->holdingObjectiveItem < ENTITYNUM_WORLD )
01617         {//holding Siege item
01618                 if ( g_entities[self->client->holdingObjectiveItem].genericValue15 )
01619                 {//disables force powers
01620                         return;
01621                 }
01622         }
01623 
01624         self->client->ps.forceAllowDeactivateTime = level.time + 1500;
01625 
01626         WP_ForcePowerStart( self, FP_SPEED, forceDuration );
01627         G_Sound( self, CHAN_BODY, G_SoundIndex("sound/weapons/force/speed.wav") );
01628         G_Sound( self, TRACK_CHANNEL_2, speedLoopSound );
01629 }
01630 
01631 void ForceSeeing( gentity_t *self )
01632 {
01633         if ( self->health <= 0 )
01634         {
01635                 return;
01636         }
01637 
01638         if (self->client->ps.forceAllowDeactivateTime < level.time &&
01639                 (self->client->ps.fd.forcePowersActive & (1 << FP_SEE)) )
01640         {
01641                 WP_ForcePowerStop( self, FP_SEE );
01642                 return;
01643         }
01644 
01645         if ( !WP_ForcePowerUsable( self, FP_SEE ) )
01646         {
01647                 return;
01648         }
01649 
01650         self->client->ps.forceAllowDeactivateTime = level.time + 1500;
01651 
01652         WP_ForcePowerStart( self, FP_SEE, 0 );
01653 
01654         G_Sound( self, CHAN_AUTO, G_SoundIndex("sound/weapons/force/see.wav") );
01655         G_Sound( self, TRACK_CHANNEL_5, seeLoopSound );
01656 }
01657 
01658 void ForceProtect( gentity_t *self )
01659 {
01660         if ( self->health <= 0 )
01661         {
01662                 return;
01663         }
01664 
01665         if (self->client->ps.forceAllowDeactivateTime < level.time &&
01666                 (self->client->ps.fd.forcePowersActive & (1 << FP_PROTECT)) )
01667         {
01668                 WP_ForcePowerStop( self, FP_PROTECT );
01669                 return;
01670         }
01671 
01672         if ( !WP_ForcePowerUsable( self, FP_PROTECT ) )
01673         {
01674                 return;
01675         }
01676 
01677         // Make sure to turn off Force Rage and Force Absorb.
01678         if (self->client->ps.fd.forcePowersActive & (1 << FP_RAGE) )
01679         {
01680                 WP_ForcePowerStop( self, FP_RAGE );
01681         }
01682         if (self->client->ps.fd.forcePowersActive & (1 << FP_ABSORB) )
01683         {
01684                 WP_ForcePowerStop( self, FP_ABSORB );
01685         }
01686 
01687         self->client->ps.forceAllowDeactivateTime = level.time + 1500;
01688 
01689         WP_ForcePowerStart( self, FP_PROTECT, 0 );
01690         G_PreDefSound(self->client->ps.origin, PDSOUND_PROTECT);
01691         G_Sound( self, TRACK_CHANNEL_3, protectLoopSound );
01692 }
01693 
01694 void ForceAbsorb( gentity_t *self )
01695 {
01696         if ( self->health <= 0 )
01697         {
01698                 return;
01699         }
01700 
01701         if (self->client->ps.forceAllowDeactivateTime < level.time &&
01702                 (self->client->ps.fd.forcePowersActive & (1 << FP_ABSORB)) )
01703         {
01704                 WP_ForcePowerStop( self, FP_ABSORB );
01705                 return;
01706         }
01707 
01708         if ( !WP_ForcePowerUsable( self, FP_ABSORB ) )
01709         {
01710                 return;
01711         }
01712 
01713         // Make sure to turn off Force Rage and Force Protection.
01714         if (self->client->ps.fd.forcePowersActive & (1 << FP_RAGE) )
01715         {
01716                 WP_ForcePowerStop( self, FP_RAGE );
01717         }
01718         if (self->client->ps.fd.forcePowersActive & (1 << FP_PROTECT) )
01719         {
01720                 WP_ForcePowerStop( self, FP_PROTECT );
01721         }
01722 
01723         self->client->ps.forceAllowDeactivateTime = level.time + 1500;
01724 
01725         WP_ForcePowerStart( self, FP_ABSORB, 0 );
01726         G_PreDefSound(self->client->ps.origin, PDSOUND_ABSORB);
01727         G_Sound( self, TRACK_CHANNEL_3, absorbLoopSound );
01728 }
01729 
01730 void ForceRage( gentity_t *self )
01731 {
01732         if ( self->health <= 0 )
01733         {
01734                 return;
01735         }
01736 
01737         if (self->client->ps.forceAllowDeactivateTime < level.time &&
01738                 (self->client->ps.fd.forcePowersActive & (1 << FP_RAGE)) )
01739         {
01740                 WP_ForcePowerStop( self, FP_RAGE );
01741                 return;
01742         }
01743 
01744         if ( !WP_ForcePowerUsable( self, FP_RAGE ) )
01745         {
01746                 return;
01747         }
01748 
01749         if (self->client->ps.fd.forceRageRecoveryTime >= level.time)
01750         {
01751                 return;
01752         }
01753 
01754         if (self->health < 10)
01755         {
01756                 return;
01757         }
01758 
01759         // Make sure to turn off Force Protection and Force Absorb.
01760         if (self->client->ps.fd.forcePowersActive & (1 << FP_PROTECT) )
01761         {
01762                 WP_ForcePowerStop( self, FP_PROTECT );
01763         }
01764         if (self->client->ps.fd.forcePowersActive & (1 << FP_ABSORB) )
01765         {
01766                 WP_ForcePowerStop( self, FP_ABSORB );
01767         }
01768 
01769         self->client->ps.forceAllowDeactivateTime = level.time + 1500;
01770 
01771         WP_ForcePowerStart( self, FP_RAGE, 0 );
01772 
01773         G_Sound( self, TRACK_CHANNEL_4, G_SoundIndex("sound/weapons/force/rage.wav") );
01774         G_Sound( self, TRACK_CHANNEL_3, rageLoopSound );
01775 }
01776 
01777 void ForceLightning( gentity_t *self )
01778 {
01779         if ( self->health <= 0 )
01780         {
01781                 return;
01782         }
01783         if ( self->client->ps.fd.forcePower < 25 || !WP_ForcePowerUsable( self, FP_LIGHTNING ) )
01784         {
01785                 return;
01786         }
01787         if ( self->client->ps.fd.forcePowerDebounce[FP_LIGHTNING] > level.time )
01788         {//stops it while using it and also after using it, up to 3 second delay
01789                 return;
01790         }
01791 
01792         if (self->client->ps.forceHandExtend != HANDEXTEND_NONE)
01793         {
01794                 return;
01795         }
01796 
01797         if (self->client->ps.weaponTime > 0)
01798         {
01799                 return;
01800         }
01801 
01802         //Shoot lightning from hand
01803         //using grip anim now, to extend the burst time
01804         self->client->ps.forceHandExtend = HANDEXTEND_FORCE_HOLD;
01805         self->client->ps.forceHandExtendTime = level.time + 20000;
01806 
01807         G_Sound( self, CHAN_BODY, G_SoundIndex("sound/weapons/force/lightning") );
01808         
01809         WP_ForcePowerStart( self, FP_LIGHTNING, 500 );
01810 }
01811 
01812 void ForceLightningDamage( gentity_t *self, gentity_t *traceEnt, vec3_t dir, vec3_t impactPoint )
01813 {
01814         self->client->dangerTime = level.time;
01815         self->client->ps.eFlags &= ~EF_INVULNERABLE;
01816         self->client->invulnerableTimer = 0;
01817 
01818         if ( traceEnt && traceEnt->takedamage )
01819         {
01820                 if (!traceEnt->client && traceEnt->s.eType == ET_NPC)
01821                 { //g2animent
01822                         if (traceEnt->s.genericenemyindex < level.time)
01823                         {
01824                                 traceEnt->s.genericenemyindex = level.time + 2000;
01825                         }
01826                 }
01827                 if ( traceEnt->client )
01828                 {//an enemy or object
01829                         if (traceEnt->client->noLightningTime >= level.time)
01830                         { //give them power and don't hurt them.
01831                                 traceEnt->client->ps.fd.forcePower++;
01832                                 if (traceEnt->client->ps.fd.forcePower > 100)
01833                                 {
01834                                         traceEnt->client->ps.fd.forcePower = 100;
01835                                 }
01836                                 return;
01837                         }
01838                         if (ForcePowerUsableOn(self, traceEnt, FP_LIGHTNING))
01839                         {
01840                                 int     dmg = Q_irand(1,2); //Q_irand( 1, 3 );
01841                                 
01842                                 int modPowerLevel = -1;
01843                                 
01844                                 if (traceEnt->client)
01845                                 {
01846                                         modPowerLevel = WP_AbsorbConversion(traceEnt, traceEnt->client->ps.fd.forcePowerLevel[FP_ABSORB], self, FP_LIGHTNING, self->client->ps.fd.forcePowerLevel[FP_LIGHTNING], 1);
01847                                 }
01848 
01849                                 if (modPowerLevel != -1)
01850                                 {
01851                                         if (!modPowerLevel)
01852                                         {
01853                                                 dmg = 0;
01854                                                 traceEnt->client->noLightningTime = level.time + 400;
01855                                         }
01856                                         else if (modPowerLevel == 1)
01857                                         {
01858                                                 dmg = 1;
01859                                                 traceEnt->client->noLightningTime = level.time + 300;
01860                                         }
01861                                         else if (modPowerLevel == 2)
01862                                         {
01863                                                 dmg = 1;
01864                                                 traceEnt->client->noLightningTime = level.time + 100;
01865                                         }
01866                                 }
01867 
01868                                 if ( self->client->ps.weapon == WP_MELEE
01869                                         && self->client->ps.fd.forcePowerLevel[FP_LIGHTNING] > FORCE_LEVEL_2 )
01870                                 {//2-handed lightning
01871                                         //jackin' 'em up, Palpatine-style
01872                                         dmg *= 2;
01873                                 }
01874 
01875                                 if (dmg)
01876                                 {
01877                                         //rww - Shields can now absorb lightning too.
01878                                         G_Damage( traceEnt, self, self, dir, impactPoint, dmg, 0, MOD_FORCE_DARK );
01879                                 }
01880                                 if ( traceEnt->client )
01881                                 {
01882                                         if ( !Q_irand( 0, 2 ) )
01883                                         {
01884                                                 G_Sound( traceEnt, CHAN_BODY, G_SoundIndex( va("sound/weapons/force/lightninghit%i", Q_irand(1, 3) )) );
01885                                         }
01886 
01887                                         if (traceEnt->client->ps.electrifyTime < (level.time + 400))
01888                                         { //only update every 400ms to reduce bandwidth usage (as it is passing a 32-bit time value)
01889                                                 traceEnt->client->ps.electrifyTime = level.time + 800;
01890                                         }
01891                                         if ( traceEnt->client->ps.powerups[PW_CLOAKED] )
01892                                         {//disable cloak temporarily
01893                                                 Jedi_Decloak( traceEnt );
01894                                                 traceEnt->client->cloakToggleTime = level.time + Q_irand( 3000, 10000 );
01895                                         }
01896                                 }
01897                         }
01898                 }
01899         }
01900 }
01901 
01902 void ForceShootLightning( gentity_t *self )
01903 {
01904         trace_t tr;
01905         vec3_t  end, forward;
01906         gentity_t       *traceEnt;
01907 
01908         if ( self->health <= 0 )
01909         {
01910                 return;
01911         }
01912         AngleVectors( self->client->ps.viewangles, forward, NULL, NULL );
01913         VectorNormalize( forward );
01914 
01915         if ( self->client->ps.fd.forcePowerLevel[FP_LIGHTNING] > FORCE_LEVEL_2 )
01916         {//arc
01917                 vec3_t  center, mins, maxs, dir, ent_org, size, v;
01918                 float   radius = FORCE_LIGHTNING_RADIUS, dot, dist;
01919                 gentity_t       *entityList[MAX_GENTITIES];
01920                 int                     iEntityList[MAX_GENTITIES];
01921                 int             e, numListedEntities, i;
01922 
01923                 VectorCopy( self->client->ps.origin, center );
01924                 for ( i = 0 ; i < 3 ; i++ ) 
01925                 {
01926                         mins[i] = center[i] - radius;
01927                         maxs[i] = center[i] + radius;
01928                 }
01929                 numListedEntities = trap_EntitiesInBox( mins, maxs, iEntityList, MAX_GENTITIES );
01930 
01931                 i = 0;
01932                 while (i < numListedEntities)
01933                 {
01934                         entityList[i] = &g_entities[iEntityList[i]];
01935 
01936                         i++;
01937                 }
01938 
01939                 for ( e = 0 ; e < numListedEntities ; e++ ) 
01940                 {
01941                         traceEnt = entityList[e];
01942 
01943                         if ( !traceEnt )
01944                                 continue;
01945                         if ( traceEnt == self )
01946                                 continue;
01947                         if ( traceEnt->r.ownerNum == self->s.number && traceEnt->s.weapon != WP_THERMAL )//can push your own thermals
01948                                 continue;
01949                         if ( !traceEnt->inuse )
01950                                 continue;
01951                         if ( !traceEnt->takedamage )
01952                                 continue;
01953                         if ( traceEnt->health <= 0 )//no torturing corpses
01954                                 continue;
01955                         if ( !g_friendlyFire.integer && OnSameTeam(self, traceEnt))
01956                                 continue;
01957                         //this is all to see if we need to start a saber attack, if it's in flight, this doesn't matter
01958                         // find the distance from the edge of the bounding box
01959                         for ( i = 0 ; i < 3 ; i++ ) 
01960                         {
01961                                 if ( center[i] < traceEnt->r.absmin[i] ) 
01962                                 {
01963                                         v[i] = traceEnt->r.absmin[i] - center[i];
01964                                 } else if ( center[i] > traceEnt->r.absmax[i] ) 
01965                                 {
01966                                         v[i] = center[i] - traceEnt->r.absmax[i];
01967                                 } else 
01968                                 {
01969                                         v[i] = 0;
01970                                 }
01971                         }
01972 
01973                         VectorSubtract( traceEnt->r.absmax, traceEnt->r.absmin, size );
01974                         VectorMA( traceEnt->r.absmin, 0.5, size, ent_org );
01975 
01976                         //see if they're in front of me
01977                         //must be within the forward cone
01978                         VectorSubtract( ent_org, center, dir );
01979                         VectorNormalize( dir );
01980                         if ( (dot = DotProduct( dir, forward )) < 0.5 )
01981                                 continue;
01982 
01983                         //must be close enough
01984                         dist = VectorLength( v );
01985                         if ( dist >= radius ) 
01986                         {
01987                                 continue;
01988                         }
01989                 
01990                         //in PVS?
01991                         if ( !traceEnt->r.bmodel && !trap_InPVS( ent_org, self->client->ps.origin ) )
01992                         {//must be in PVS
01993                                 continue;
01994                         }
01995 
01996                         //Now check and see if we can actually hit it
01997                         trap_Trace( &tr, self->client->ps.origin, vec3_origin, vec3_origin, ent_org, self->s.number, MASK_SHOT );
01998                         if ( tr.fraction < 1.0f && tr.entityNum != traceEnt->s.number )
01999                         {//must have clear LOS
02000                                 continue;
02001                         }
02002 
02003                         // ok, we are within the radius, add us to the incoming list
02004                         ForceLightningDamage( self, traceEnt, dir, ent_org );
02005                 }
02006         }
02007         else
02008         {//trace-line
02009                 VectorMA( self->client->ps.origin, 2048, forward, end );
02010                 
02011                 trap_Trace( &tr, self->client->ps.origin, vec3_origin, vec3_origin, end, self->s.number, MASK_SHOT );
02012                 if ( tr.entityNum == ENTITYNUM_NONE || tr.fraction == 1.0 || tr.allsolid || tr.startsolid )
02013                 {
02014                         return;
02015                 }
02016                 
02017                 traceEnt = &g_entities[tr.entityNum];
02018                 ForceLightningDamage( self, traceEnt, forward, tr.endpos );
02019         }
02020 }
02021 
02022 void ForceDrain( gentity_t *self )
02023 {
02024         if ( self->health <= 0 )
02025         {
02026                 return;
02027         }
02028 
02029         if (self->client->ps.forceHandExtend != HANDEXTEND_NONE)
02030         {
02031                 return;
02032         }
02033 
02034         if (self->client->ps.weaponTime > 0)
02035         {
02036                 return;
02037         }
02038 
02039         if ( self->client->ps.fd.forcePower < 25 || !WP_ForcePowerUsable( self, FP_DRAIN ) )
02040         {
02041                 return;
02042         }
02043         if ( self->client->ps.fd.forcePowerDebounce[FP_DRAIN] > level.time )
02044         {//stops it while using it and also after using it, up to 3 second delay
02045                 return;
02046         }
02047 
02048 //      self->client->ps.forceHandExtend = HANDEXTEND_FORCEPUSH;
02049 //      self->client->ps.forceHandExtendTime = level.time + 1000;
02050         self->client->ps.forceHandExtend = HANDEXTEND_FORCE_HOLD;
02051         self->client->ps.forceHandExtendTime = level.time + 20000;
02052 
02053         G_Sound( self, CHAN_BODY, G_SoundIndex("sound/weapons/force/drain.wav") );
02054         
02055         WP_ForcePowerStart( self, FP_DRAIN, 500 );
02056 }
02057 
02058 void ForceDrainDamage( gentity_t *self, gentity_t *traceEnt, vec3_t dir, vec3_t impactPoint )
02059 {
02060         gentity_t *tent;
02061 
02062         self->client->dangerTime = level.time;
02063         self->client->ps.eFlags &= ~EF_INVULNERABLE;
02064         self->client->invulnerableTimer = 0;
02065 
02066         if ( traceEnt && traceEnt->takedamage )
02067         {
02068                 if ( traceEnt->client && (!OnSameTeam(self, traceEnt) || g_friendlyFire.integer) && self->client->ps.fd.forceDrainTime < level.time && traceEnt->client->ps.fd.forcePower )
02069                 {//an enemy or object
02070                         if (!traceEnt->client && traceEnt->s.eType == ET_NPC)
02071                         { //g2animent
02072                                 if (traceEnt->s.genericenemyindex < level.time)
02073                                 {
02074                                         traceEnt->s.genericenemyindex = level.time + 2000;
02075                                 }
02076                         }
02077                         if (ForcePowerUsableOn(self, traceEnt, FP_DRAIN))
02078                         {
02079                                 int modPowerLevel = -1;
02080                                 int     dmg = 0; //Q_irand( 1, 3 );
02081                                 if (self->client->ps.fd.forcePowerLevel[FP_DRAIN] == FORCE_LEVEL_1)
02082                                 {
02083                                         dmg = 2; //because it's one-shot
02084                                 }
02085                                 else if (self->client->ps.fd.forcePowerLevel[FP_DRAIN] == FORCE_LEVEL_2)
02086                                 {
02087                                         dmg = 3;
02088                                 }
02089                                 else if (self->client->ps.fd.forcePowerLevel[FP_DRAIN] == FORCE_LEVEL_3)
02090                                 {
02091                                         dmg = 4;
02092                                 }
02093                         
02094                                 if (traceEnt->client)
02095                                 {
02096                                         modPowerLevel = WP_AbsorbConversion(traceEnt, traceEnt->client->ps.fd.forcePowerLevel[FP_ABSORB], self, FP_DRAIN, self->client->ps.fd.forcePowerLevel[FP_DRAIN], 1);
02097                                 }
02098 
02099                                 if (modPowerLevel != -1)
02100                                 {
02101                                         if (!modPowerLevel)
02102                                         {
02103                                                 dmg = 0;
02104                                         }
02105                                         else if (modPowerLevel == 1)
02106                                         {
02107                                                 dmg = 1;
02108                                         }
02109                                         else if (modPowerLevel == 2)
02110                                         {
02111                                                 dmg = 2;
02112                                         }
02113                                 }
02114                                 //G_Damage( traceEnt, self, self, dir, impactPoint, dmg, 0, MOD_FORCE_DARK );
02115 
02116                                 if (dmg)
02117                                 {
02118                                         traceEnt->client->ps.fd.forcePower -= (dmg);
02119                                 }
02120                                 if (traceEnt->client->ps.fd.forcePower < 0)
02121                                 {
02122                                         traceEnt->client->ps.fd.forcePower = 0;
02123                                 }
02124 
02125                                 if (self->client->ps.stats[STAT_HEALTH] < self->client->ps.stats[STAT_MAX_HEALTH] &&
02126                                         self->health > 0 && self->client->ps.stats[STAT_HEALTH] > 0)
02127                                 {
02128                                         self->health += dmg;
02129                                         if (self->health > self->client->ps.stats[STAT_MAX_HEALTH])
02130                                         {
02131                                                 self->health = self->client->ps.stats[STAT_MAX_HEALTH];
02132                                         }
02133                                         self->client->ps.stats[STAT_HEALTH] = self->health;
02134                                 }
02135 
02136                                 traceEnt->client->ps.fd.forcePowerRegenDebounceTime = level.time + 800; //don't let the client being drained get force power back right away
02137 
02138                                 //Drain the standard amount since we just drained someone else
02139 
02140                                 /*
02141                                 if (self->client->ps.fd.forcePowerLevel[FP_DRAIN] == FORCE_LEVEL_1)
02142                                 {
02143                                         BG_ForcePowerDrain( &self->client->ps, FP_DRAIN, 0 );
02144                                 }
02145                                 else
02146                                 {
02147                                         BG_ForcePowerDrain( &self->client->ps, FP_DRAIN, forcePowerNeeded[self->client->ps.fd.forcePowerLevel[FP_DRAIN]][FP_DRAIN]/5 );
02148                                 }
02149 
02150                                 if (self->client->ps.fd.forcePowerLevel[FP_DRAIN] == FORCE_LEVEL_1)
02151                                 {
02152                                         self->client->ps.fd.forceDrainTime = level.time + 100;
02153                                 }
02154                                 else
02155                                 {
02156                                         self->client->ps.fd.forceDrainTime = level.time + 20;
02157                                 }
02158 
02159                                 if ( traceEnt->client )
02160                                 {
02161                                         if ( !Q_irand( 0, 2 ) )
02162                                         {
02163                                                 //G_Sound( traceEnt, CHAN_BODY, G_SoundIndex( "sound/weapons/force/lightninghit.wav" ) );
02164                                         }
02165                                 //      traceEnt->s.powerups |= ( 1 << PW_DISINT_1 );
02166 
02167                                 //      traceEnt->client->ps.powerups[PW_DISINT_1] = level.time + 500;
02168                                 }
02169                                 */
02170 
02171                                 if (traceEnt->client->forcePowerSoundDebounce < level.time)
02172                                 {
02173                                         tent = G_TempEntity( impactPoint, EV_FORCE_DRAINED);
02174                                         tent->s.eventParm = DirToByte(dir);
02175                                         tent->s.owner = traceEnt->s.number;
02176 
02177                                         traceEnt->client->forcePowerSoundDebounce = level.time + 400;
02178                                 }
02179                         }
02180                 }
02181         }
02182 }
02183 
02184 int ForceShootDrain( gentity_t *self )
02185 {
02186         trace_t tr;
02187         vec3_t  end, forward;
02188         gentity_t       *traceEnt;
02189         int                     gotOneOrMore = 0;
02190 
02191         if ( self->health <= 0 )
02192         {
02193                 return 0;
02194         }
02195         AngleVectors( self->client->ps.viewangles, forward, NULL, NULL );
02196         VectorNormalize( forward );
02197 
02198         if ( self->client->ps.fd.forcePowerLevel[FP_DRAIN] > FORCE_LEVEL_2 )
02199         {//arc
02200                 vec3_t  center, mins, maxs, dir, ent_org, size, v;
02201                 float   radius = MAX_DRAIN_DISTANCE, dot, dist;
02202                 gentity_t       *entityList[MAX_GENTITIES];
02203                 int                     iEntityList[MAX_GENTITIES];
02204                 int             e, numListedEntities, i;
02205 
02206                 VectorCopy( self->client->ps.origin, center );
02207                 for ( i = 0 ; i < 3 ; i++ ) 
02208                 {
02209                         mins[i] = center[i] - radius;
02210                         maxs[i] = center[i] + radius;
02211                 }
02212                 numListedEntities = trap_EntitiesInBox( mins, maxs, iEntityList, MAX_GENTITIES );
02213 
02214                 i = 0;
02215                 while (i < numListedEntities)
02216                 {
02217                         entityList[i] = &g_entities[iEntityList[i]];
02218 
02219                         i++;
02220                 }
02221 
02222                 for ( e = 0 ; e < numListedEntities ; e++ ) 
02223                 {
02224                         traceEnt = entityList[e];
02225 
02226                         if ( !traceEnt )
02227                                 continue;
02228                         if ( traceEnt == self )
02229                                 continue;
02230                         if ( !traceEnt->inuse )
02231                                 continue;
02232                         if ( !traceEnt->takedamage )
02233                                 continue;
02234                         if ( traceEnt->health <= 0 )//no torturing corpses
02235                                 continue;
02236                         if ( !traceEnt->client )
02237                                 continue;
02238                         if ( !traceEnt->client->ps.fd.forcePower )
02239                                 continue;
02240                         if (OnSameTeam(self, traceEnt) && !g_friendlyFire.integer)
02241                                 continue;
02242                         //this is all to see if we need to start a saber attack, if it's in flight, this doesn't matter
02243                         // find the distance from the edge of the bounding box
02244                         for ( i = 0 ; i < 3 ; i++ ) 
02245                         {
02246                                 if ( center[i] < traceEnt->r.absmin[i] ) 
02247                                 {
02248                                         v[i] = traceEnt->r.absmin[i] - center[i];
02249                                 } else if ( center[i] > traceEnt->r.absmax[i] ) 
02250                                 {
02251                                         v[i] = center[i] - traceEnt->r.absmax[i];
02252                                 } else 
02253                                 {
02254                                         v[i] = 0;
02255                                 }
02256                         }
02257 
02258                         VectorSubtract( traceEnt->r.absmax, traceEnt->r.absmin, size );
02259                         VectorMA( traceEnt->r.absmin, 0.5, size, ent_org );
02260 
02261                         //see if they're in front of me
02262                         //must be within the forward cone
02263                         VectorSubtract( ent_org, center, dir );
02264                         VectorNormalize( dir );
02265                         if ( (dot = DotProduct( dir, forward )) < 0.5 )
02266                                 continue;
02267 
02268                         //must be close enough
02269                         dist = VectorLength( v );
02270                         if ( dist >= radius ) 
02271                         {
02272                                 continue;
02273                         }
02274                 
02275                         //in PVS?
02276                         if ( !traceEnt->r.bmodel && !trap_InPVS( ent_org, self->client->ps.origin ) )
02277                         {//must be in PVS
02278                                 continue;
02279                         }
02280 
02281                         //Now check and see if we can actually hit it
02282                         trap_Trace( &tr, self->client->ps.origin, vec3_origin, vec3_origin, ent_org, self->s.number, MASK_SHOT );
02283                         if ( tr.fraction < 1.0f && tr.entityNum != traceEnt->s.number )
02284                         {//must have clear LOS
02285                                 continue;
02286                         }
02287 
02288                         // ok, we are within the radius, add us to the incoming list
02289                         ForceDrainDamage( self, traceEnt, dir, ent_org );
02290                         gotOneOrMore = 1;
02291                 }
02292         }
02293         else
02294         {//trace-line
02295                 VectorMA( self->client->ps.origin, 2048, forward, end );
02296                 
02297                 trap_Trace( &tr, self->client->ps.origin, vec3_origin, vec3_origin, end, self->s.number, MASK_SHOT );
02298                 if ( tr.entityNum == ENTITYNUM_NONE || tr.fraction == 1.0 || tr.allsolid || tr.startsolid || !g_entities[tr.entityNum].client || !g_entities[tr.entityNum].inuse )
02299                 {
02300                         return 0;
02301                 }
02302                 
02303                 traceEnt = &g_entities[tr.entityNum];
02304                 ForceDrainDamage( self, traceEnt, forward, tr.endpos );
02305                 gotOneOrMore = 1;
02306         }
02307 
02308         self->client->ps.activeForcePass = self->client->ps.fd.forcePowerLevel[FP_DRAIN] + FORCE_LEVEL_3;
02309 
02310         BG_ForcePowerDrain( &self->client->ps, FP_DRAIN, 5 ); //used to be 1, but this did, too, anger the God of Balance.
02311 
02312         self->client->ps.fd.forcePowerRegenDebounceTime = level.time + 500;
02313 
02314         return gotOneOrMore;
02315 }
02316 
02317 void ForceJumpCharge( gentity_t *self, usercmd_t *ucmd )
02318 { //I guess this is unused now. Was used for the "charge" jump type.
02319         float forceJumpChargeInterval = forceJumpStrength[0] / (FORCE_JUMP_CHARGE_TIME/FRAMETIME);
02320 
02321         if ( self->health <= 0 )
02322         {
02323                 return;
02324         }
02325 
02326         if (!self->client->ps.fd.forceJumpCharge && self->client->ps.groundEntityNum == ENTITYNUM_NONE)
02327         {
02328                 return;
02329         }
02330 
02331         if (self->client->ps.fd.forcePower < forcePowerNeeded[self->client->ps.fd.forcePowerLevel[FP_LEVITATION]][FP_LEVITATION])
02332         {
02333                 G_MuteSound(self->client->ps.fd.killSoundEntIndex[TRACK_CHANNEL_1-50], CHAN_VOICE);
02334                 return;
02335         }
02336 
02337         if (!self->client->ps.fd.forceJumpCharge)
02338         {
02339                 self->client->ps.fd.forceJumpAddTime = 0;
02340         }
02341 
02342         if (self->client->ps.fd.forceJumpAddTime >= level.time)
02343         {
02344                 return;
02345         }
02346 
02347         //need to play sound
02348         if ( !self->client->ps.fd.forceJumpCharge )
02349         {
02350                 G_Sound( self, TRACK_CHANNEL_1, G_SoundIndex("sound/weapons/force/jumpbuild.wav") );
02351         }
02352 
02353         //Increment
02354         if (self->client->ps.fd.forceJumpAddTime < level.time)
02355         {
02356                 self->client->ps.fd.forceJumpCharge += forceJumpChargeInterval*50;
02357                 self->client->ps.fd.forceJumpAddTime = level.time + 500;
02358         }
02359 
02360         //clamp to max strength for current level
02361         if ( self->client->ps.fd.forceJumpCharge > forceJumpStrength[self->client->ps.fd.forcePowerLevel[FP_LEVITATION]] )
02362         {
02363                 self->client->ps.fd.forceJumpCharge = forceJumpStrength[self->client->ps.fd.forcePowerLevel[FP_LEVITATION]];
02364                 G_MuteSound(self->client->ps.fd.killSoundEntIndex[TRACK_CHANNEL_1-50], CHAN_VOICE);
02365         }
02366 
02367         //clamp to max available force power
02368         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 )
02369         {//can't use more than you have
02370                 G_MuteSound(self->client->ps.fd.killSoundEntIndex[TRACK_CHANNEL_1-50], CHAN_VOICE);
02371                 self->client->ps.fd.forceJumpCharge = self->client->ps.fd.forcePower*forceJumpChargeInterval/(FORCE_JUMP_CHARGE_TIME/FRAMETIME);
02372         }
02373         
02374         //G_Printf("%f\n", self->client->ps.fd.forceJumpCharge);
02375 }
02376 
02377 int WP_GetVelocityForForceJump( gentity_t *self, vec3_t jumpVel, usercmd_t *ucmd )
02378 {
02379         float pushFwd = 0, pushRt = 0;
02380         vec3_t  view, forward, right;
02381         VectorCopy( self->client->ps.viewangles, view );
02382         view[0] = 0;
02383         AngleVectors( view, forward, right, NULL );
02384         if ( ucmd->forwardmove && ucmd->rightmove )
02385         {
02386                 if ( ucmd->forwardmove > 0 )
02387                 {
02388                         pushFwd = 50;
02389                 }
02390                 else
02391                 {
02392                         pushFwd = -50;
02393                 }
02394                 if ( ucmd->rightmove > 0 )
02395                 {
02396                         pushRt = 50;
02397                 }
02398                 else
02399                 {
02400                         pushRt = -50;
02401                 }
02402         }
02403         else if ( ucmd->forwardmove || ucmd->rightmove )
02404         {
02405                 if ( ucmd->forwardmove > 0 )
02406                 {
02407                         pushFwd = 100;
02408                 }
02409                 else if ( ucmd->forwardmove < 0 )
02410                 {
02411                         pushFwd = -100;
02412                 }
02413                 else if ( ucmd->rightmove > 0 )
02414                 {
02415                         pushRt = 100;
02416                 }
02417                 else if ( ucmd->rightmove < 0 )
02418                 {
02419                         pushRt = -100;
02420                 }
02421         }
02422 
02423         G_MuteSound(self->client->ps.fd.killSoundEntIndex[TRACK_CHANNEL_1-50], CHAN_VOICE);
02424 
02425         G_PreDefSound(self->client->ps.origin, PDSOUND_FORCEJUMP);
02426 
02427         if (self->client->ps.fd.forceJumpCharge < JUMP_VELOCITY+40)
02428         { //give him at least a tiny boost from just a tap
02429                 self->client->ps.fd.forceJumpCharge = JUMP_VELOCITY+400;
02430         }
02431 
02432         if (self->client->ps.velocity[2] < -30)
02433         { //so that we can get a good boost when force jumping in a fall
02434                 self->client->ps.velocity[2] = -30;
02435         }
02436 
02437         VectorMA( self->client->ps.velocity, pushFwd, forward, jumpVel );
02438         VectorMA( self->client->ps.velocity, pushRt, right, jumpVel );
02439         jumpVel[2] += self->client->ps.fd.forceJumpCharge;
02440         if ( pushFwd > 0 && self->client->ps.fd.forceJumpCharge > 200 )
02441         {
02442                 return FJ_FORWARD;
02443         }
02444         else if ( pushFwd < 0 && self->client->ps.fd.forceJumpCharge > 200 )
02445         {
02446                 return FJ_BACKWARD;
02447         }
02448         else if ( pushRt > 0 && self->client->ps.fd.forceJumpCharge > 200 )
02449         {
02450                 return FJ_RIGHT;
02451         }
02452         else if ( pushRt < 0 && self->client->ps.fd.forceJumpCharge > 200 )
02453         {
02454                 return FJ_LEFT;
02455         }
02456         else
02457         {
02458                 return FJ_UP;
02459         }
02460 }
02461 
02462 void ForceJump( gentity_t *self, usercmd_t *ucmd )
02463 {
02464         float forceJumpChargeInterval;
02465         vec3_t  jumpVel;
02466 
02467         if ( self->client->ps.fd.forcePowerDuration[FP_LEVITATION] > level.time )
02468         {
02469                 return;
02470         }
02471         if ( !WP_ForcePowerUsable( self, FP_LEVITATION ) )
02472         {
02473                 return;
02474         }
02475         if ( self->s.groundEntityNum == ENTITYNUM_NONE )
02476         {
02477                 return;
02478         }
02479         if ( self->health <= 0 )
02480         {
02481                 return;
02482         }
02483 
02484         self->client->fjDidJump = qtrue;
02485 
02486         forceJumpChargeInterval = forceJumpStrength[self->client->ps.fd.forcePowerLevel[FP_LEVITATION]]/(FORCE_JUMP_CHARGE_TIME/FRAMETIME);
02487 
02488         WP_GetVelocityForForceJump( self, jumpVel, ucmd );
02489 
02490         //FIXME: sound effect
02491         self->client->ps.fd.forceJumpZStart = self->client->ps.origin[2];//remember this for when we land
02492         VectorCopy( jumpVel, self->client->ps.velocity );
02493         //wasn't allowing them to attack when jumping, but that was annoying
02494         //self->client->ps.weaponTime = self->client->ps.torsoAnimTimer;
02495 
02496         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] );
02497         //self->client->ps.fd.forcePowerDuration[FP_LEVITATION] = level.time + self->client->ps.weaponTime;
02498         self->client->ps.fd.forceJumpCharge = 0;
02499         self->client->ps.forceJumpFlip = qtrue;
02500 }
02501 
02502 void WP_AddAsMindtricked(forcedata_t *fd, int entNum)
02503 {
02504         if (!fd)
02505         {
02506                 return;
02507         }
02508 
02509         if (entNum > 47)
02510         {
02511                 fd->forceMindtrickTargetIndex4 |= (1 << (entNum-48));
02512         }
02513         else if (entNum > 31)
02514         {
02515                 fd->forceMindtrickTargetIndex3 |= (1 << (entNum-32));
02516         }
02517         else if (entNum > 15)
02518         {
02519                 fd->forceMindtrickTargetIndex2 |= (1 << (entNum-16));
02520         }
02521         else
02522         {
02523                 fd->forceMindtrickTargetIndex |= (1 << entNum);
02524         }
02525 }
02526 
02527 qboolean ForceTelepathyCheckDirectNPCTarget( gentity_t *self, trace_t *tr, qboolean *tookPower )
02528 {
02529         gentity_t       *traceEnt;
02530         qboolean        targetLive = qfalse, mindTrickDone = qfalse;
02531         vec3_t          tfrom, tto, fwd;
02532         float           radius = MAX_TRICK_DISTANCE;
02533 
02534         //Check for a direct usage on NPCs first
02535         VectorCopy(self->client->ps.origin, tfrom);
02536         tfrom[2] += self->client->ps.viewheight;
02537         AngleVectors(self->client->ps.viewangles, fwd, NULL, NULL);
02538         tto[0] = tfrom[0] + fwd[0]*radius/2;
02539         tto[1] = tfrom[1] + fwd[1]*radius/2;
02540         tto[2] = tfrom[2] + fwd[2]*radius/2;
02541 
02542         trap_Trace( tr, tfrom, NULL, NULL, tto, self->s.number, MASK_PLAYERSOLID );
02543         
02544         if ( tr->entityNum == ENTITYNUM_NONE 
02545                 || tr->fraction == 1.0f
02546                 || tr->allsolid 
02547                 || tr->startsolid )
02548         {
02549                 return qfalse;
02550         }
02551         
02552         traceEnt = &g_entities[tr->entityNum];
02553         
02554         if( traceEnt->NPC 
02555                 && traceEnt->NPC->scriptFlags & SCF_NO_FORCE )
02556         {
02557                 return qfalse;
02558         }
02559 
02560         if ( traceEnt && traceEnt->client  )
02561         {
02562                 switch ( traceEnt->client->NPC_class )
02563                 {
02564                 case CLASS_GALAKMECH://cant grip him, he's in armor
02565                 case CLASS_ATST://much too big to grip!
02566                 //no droids either
02567                 case CLASS_PROBE:
02568                 case CLASS_GONK:
02569                 case CLASS_R2D2:
02570                 case CLASS_R5D2:
02571                 case CLASS_MARK1:
02572                 case CLASS_MARK2:
02573                 case CLASS_MOUSE:
02574                 case CLASS_SEEKER:
02575                 case CLASS_REMOTE:
02576                 case CLASS_PROTOCOL:
02577                 case CLASS_BOBAFETT:
02578                 case CLASS_RANCOR:
02579                         break;
02580                 default:
02581                         targetLive = qtrue;
02582                         break;
02583                 }
02584         }
02585 
02586         if ( traceEnt->s.number < MAX_CLIENTS )
02587         {//a regular client
02588                 return qfalse;
02589         }
02590 
02591         if ( targetLive && traceEnt->NPC )
02592         {//hit an organic non-player
02593                 vec3_t  eyeDir;
02594                 if ( G_ActivateBehavior( traceEnt, BSET_MINDTRICK ) )
02595                 {//activated a script on him
02596                         //FIXME: do the visual sparkles effect on their heads, still?
02597                         WP_ForcePowerStart( self, FP_TELEPATHY, 0 );
02598                 }
02599                 else if ( (self->NPC && traceEnt->client->playerTeam != self->client->playerTeam)
02600                         || (!self->NPC && traceEnt->client->playerTeam != self->client->sess.sessionTeam) )
02601                 {//an enemy
02602                         int override = 0;
02603                         if ( (traceEnt->NPC->scriptFlags&SCF_NO_MIND_TRICK) )
02604                         {
02605                         }
02606                         else if ( traceEnt->s.weapon != WP_SABER 
02607                                 && traceEnt->client->NPC_class != CLASS_REBORN )
02608                         {//haha!  Jedi aren't easily confused!
02609                                 if ( self->client->ps.fd.forcePowerLevel[FP_TELEPATHY] > FORCE_LEVEL_2 )
02610                                 {//turn them to our side
02611                                         //if mind trick 3 and aiming at an enemy need more force power
02612                                         if ( traceEnt->s.weapon != WP_NONE )
02613                                         {//don't charm people who aren't capable of fighting... like ugnaughts and droids
02614                                                 int newPlayerTeam, newEnemyTeam;
02615 
02616                                                 if ( traceEnt->enemy )
02617                                                 {
02618                                                         G_ClearEnemy( traceEnt );
02619                                                 }
02620                                                 if ( traceEnt->NPC )
02621                                                 {
02622                                                         //traceEnt->NPC->tempBehavior = BS_FOLLOW_LEADER;
02623                                                         traceEnt->client->leader = self;
02624                                                 }
02625                                                 //FIXME: maybe pick an enemy right here?
02626                                                 if ( self->NPC )
02627                                                 {//NPC
02628                                                         newPlayerTeam = self->client->playerTeam;
02629                                                         newEnemyTeam = self->client->enemyTeam;
02630                                                 }
02631                                                 else
02632                                                 {//client/bot
02633                                                         if ( self->client->sess.sessionTeam == TEAM_BLUE )
02634                                                         {//rebel
02635                                                                 newPlayerTeam = NPCTEAM_PLAYER;
02636                                                                 newEnemyTeam = NPCTEAM_ENEMY;
02637                                                         }
02638                                                         else if ( self->client->sess.sessionTeam == TEAM_RED )
02639                                                         {//imperial
02640                                                                 newPlayerTeam = NPCTEAM_ENEMY;
02641                                                                 newEnemyTeam = NPCTEAM_PLAYER;
02642                                                         }
02643                                                         else
02644                                                         {//neutral - wan't attack anyone
02645                                                                 newPlayerTeam = NPCTEAM_NEUTRAL;
02646                                                                 newEnemyTeam = NPCTEAM_NEUTRAL;
02647                                                         }
02648                                                 }
02649                                                 //store these for retrieval later
02650                                                 traceEnt->genericValue1 = traceEnt->client->playerTeam;
02651                                                 traceEnt->genericValue2 = traceEnt->client->enemyTeam;
02652                                                 traceEnt->genericValue3 = traceEnt->s.teamowner;
02653                                                 //set the new values
02654                                                 traceEnt->client->playerTeam = newPlayerTeam;
02655                                                 traceEnt->client->enemyTeam = newEnemyTeam;
02656                                                 traceEnt->s.teamowner = newPlayerTeam;
02657                                                 //FIXME: need a *charmed* timer on this...?  Or do TEAM_PLAYERS assume that "confusion" means they should switch to team_enemy when done?
02658                                                 traceEnt->NPC->charmedTime = level.time + mindTrickTime[self->client->ps.fd.forcePowerLevel[FP_TELEPATHY]];
02659                                         }
02660                                 }
02661                                 else
02662                                 {//just confuse them
02663                                         //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?
02664                                         traceEnt->NPC->confusionTime = level.time + mindTrickTime[self->client->ps.fd.forcePowerLevel[FP_TELEPATHY]];//confused for about 10 seconds
02665                                         NPC_PlayConfusionSound( traceEnt );
02666                                         if ( traceEnt->enemy )
02667                                         {
02668                                                 G_ClearEnemy( traceEnt );
02669                                         }
02670                                 }
02671                         }
02672                         else
02673                         {
02674                                 NPC_Jedi_PlayConfusionSound( traceEnt );
02675                         }
02676                         WP_ForcePowerStart( self, FP_TELEPATHY, override );
02677                 }
02678                 else if ( traceEnt->client->playerTeam == self->client->playerTeam )
02679                 {//an ally
02680                         //maybe just have him look at you?  Respond?  Take your enemy?
02681                         if ( traceEnt->client->ps.pm_type < PM_DEAD && traceEnt->NPC!=NULL && !(traceEnt->NPC->scriptFlags&SCF_NO_RESPONSE) )
02682                         {
02683                                 NPC_UseResponse( traceEnt, self, qfalse );
02684                                 WP_ForcePowerStart( self, FP_TELEPATHY, 1 );
02685                         }
02686                 }//NOTE: no effect on TEAM_NEUTRAL?
02687                 AngleVectors( traceEnt->client->renderInfo.eyeAngles, eyeDir, NULL, NULL );
02688                 VectorNormalize( eyeDir );
02689                 G_PlayEffectID( G_EffectIndex( "force/force_touch" ), traceEnt->client->renderInfo.eyePoint, eyeDir );
02690 
02691                 //make sure this plays and that you cannot press fire for about 1 second after this
02692                 //FIXME: BOTH_FORCEMINDTRICK or BOTH_FORCEDISTRACT
02693                 //NPC_SetAnim( self, SETANIM_TORSO, BOTH_MINDTRICK1, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_RESTART|SETANIM_FLAG_HOLD );
02694                 //FIXME: build-up or delay this until in proper part of anim
02695                 mindTrickDone = qtrue;
02696         }
02697         else 
02698         {
02699                 if ( self->client->ps.fd.forcePowerLevel[FP_TELEPATHY] > FORCE_LEVEL_1 && tr->fraction * 2048 > 64 )
02700                 {//don't create a diversion less than 64 from you of if at power level 1
02701                         //use distraction anim instead
02702                         G_PlayEffectID( G_EffectIndex( "force/force_touch" ), tr->endpos, tr->plane.normal );
02703                         //FIXME: these events don't seem to always be picked up...?
02704                         AddSoundEvent( self, tr->endpos, 512, AEL_SUSPICIOUS, qtrue );//, qtrue );
02705                         AddSightEvent( self, tr->endpos, 512, AEL_SUSPICIOUS, 50 );
02706                         WP_ForcePowerStart( self, FP_TELEPATHY, 0 );
02707                         *tookPower = qtrue;
02708                 }
02709                 //NPC_SetAnim( self, SETANIM_TORSO, BOTH_MINDTRICK2, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_RESTART|SETANIM_FLAG_HOLD );
02710         }
02711         //self->client->ps.saberMove = self->client->ps.saberBounceMove = LS_READY;//don't finish whatever saber anim you may have been in
02712         self->client->ps.saberBlocked = BLOCKED_NONE;
02713         self->client->ps.weaponTime = 1000;
02714         /*
02715         if ( self->client->ps.fd.forcePowersActive&(1<<FP_SPEED) )
02716         {
02717                 self->client->ps.weaponTime = floor( self->client->ps.weaponTime * g_timescale->value );
02718         }
02719         */
02720         return qtrue;
02721 }
02722 
02723 void ForceTelepathy(gentity_t *self)
02724 {
02725         trace_t tr;
02726         vec3_t tto, thispush_org, a;
02727         vec3_t mins, maxs, fwdangles, forward, right, center;
02728         int i;
02729         float visionArc = 0;
02730         float radius = MAX_TRICK_DISTANCE;
02731         qboolean        tookPower = qfalse;
02732 
02733         if ( self->health <= 0 )
02734         {
02735                 return;
02736         }
02737 
02738         if (self->client->ps.forceHandExtend != HANDEXTEND_NONE)
02739         {
02740                 return;
02741         }
02742 
02743         if (self->client->ps.weaponTime > 0)
02744         {
02745                 return;
02746         }
02747 
02748         if (self->client->ps.powerups[PW_REDFLAG] ||
02749                 self->client->ps.powerups[PW_BLUEFLAG])
02750         { //can't mindtrick while carrying the flag
02751                 return;
02752         }
02753 
02754         if (self->client->ps.forceAllowDeactivateTime < level.time &&
02755                 (self->client->ps.fd.forcePowersActive & (1 << FP_TELEPATHY)) )
02756         {
02757                 WP_ForcePowerStop( self, FP_TELEPATHY );
02758                 return;
02759         }
02760 
02761         if ( !WP_ForcePowerUsable( self, FP_TELEPATHY ) )
02762         {
02763                 return;
02764         }
02765 
02766         if ( ForceTelepathyCheckDirectNPCTarget( self, &tr, &tookPower ) )
02767         {//hit an NPC directly
02768                 self->client->ps.forceAllowDeactivateTime = level.time + 1500;
02769                 G_Sound( self, CHAN_AUTO, G_SoundIndex("sound/weapons/force/distract.wav") );
02770                 self->client->ps.forceHandExtend = HANDEXTEND_FORCEPUSH;
02771                 self->client->ps.forceHandExtendTime = level.time + 1000;
02772                 return;
02773         }
02774 
02775         if (self->client->ps.fd.forcePowerLevel[FP_TELEPATHY] == FORCE_LEVEL_2)
02776         {
02777                 visionArc = 180;
02778         }
02779         else if (self->client->ps.fd.forcePowerLevel[FP_TELEPATHY] == FORCE_LEVEL_3)
02780         {
02781                 visionArc = 360;
02782                 radius = MAX_TRICK_DISTANCE*2.0f;
02783         }
02784 
02785         VectorCopy( self->client->ps.viewangles, fwdangles );
02786         AngleVectors( fwdangles, forward, right, NULL );
02787         VectorCopy( self->client->ps.origin, center );
02788 
02789         for ( i = 0 ; i < 3 ; i++ ) 
02790         {
02791                 mins[i] = center[i] - radius;
02792                 maxs[i] = center[i] + radius;
02793         }
02794 
02795         if (self->client->ps.fd.forcePowerLevel[FP_TELEPATHY] == FORCE_LEVEL_1)
02796         {
02797                 if (tr.fraction != 1.0 &&
02798                         tr.entityNum != ENTITYNUM_NONE &&
02799                         g_entities[tr.entityNum].inuse &&
02800                         g_entities[tr.entityNum].client &&
02801                         g_entities[tr.entityNum].client->pers.connected &&
02802                         g_entities[tr.entityNum].client->sess.sessionTeam != TEAM_SPECTATOR)
02803                 {
02804                         WP_AddAsMindtricked(&self->client->ps.fd, tr.entityNum);
02805                         if ( !tookPower )
02806                         {
02807                                 WP_ForcePowerStart( self, FP_TELEPATHY, 0 );
02808                         }
02809 
02810                         G_Sound( self, CHAN_AUTO, G_SoundIndex("sound/weapons/force/distract.wav") );
02811 
02812                         self->client->ps.forceHandExtend = HANDEXTEND_FORCEPUSH;
02813                         self->client->ps.forceHandExtendTime = level.time + 1000;
02814 
02815                         return;
02816                 }
02817                 else
02818                 {
02819                         return;
02820                 }
02821         }
02822         else    //level 2 & 3
02823         {
02824                 gentity_t *ent;
02825                 int entityList[MAX_GENTITIES];
02826                 int numListedEntities;
02827                 int e = 0;
02828                 qboolean gotatleastone = qfalse;
02829 
02830                 numListedEntities = trap_EntitiesInBox( mins, maxs, entityList, MAX_GENTITIES );
02831 
02832                 while (e < numListedEntities)
02833                 {
02834                         ent = &g_entities[entityList[e]];
02835 
02836                         if (ent)
02837                         { //not in the arc, don't consider it
02838                                 if (ent->client)
02839                                 {
02840                                         VectorCopy(ent->client->ps.origin, thispush_org);
02841                                 }
02842                                 else
02843                                 {
02844                                         VectorCopy(ent->s.pos.trBase, thispush_org);
02845                                 }
02846                                 VectorCopy(self->client->ps.origin, tto);
02847                                 tto[2] += self->client->ps.viewheight;
02848                                 VectorSubtract(thispush_org, tto, a);
02849                                 vectoangles(a, a);
02850 
02851                                 if (!ent->client)
02852                                 {
02853                                         entityList[e] = ENTITYNUM_NONE;
02854                                 }
02855                                 else if (!InFieldOfVision(self->client->ps.viewangles, visionArc, a))
02856                                 { //only bother with arc rules if the victim is a client
02857                                         entityList[e] = ENTITYNUM_NONE;
02858                                 }
02859                                 else if (!ForcePowerUsableOn(self, ent, FP_TELEPATHY))
02860                                 {
02861                                         entityList[e] = ENTITYNUM_NONE;
02862                                 }
02863                                 else if (OnSameTeam(self, ent))
02864                                 {
02865                                         entityList[e] = ENTITYNUM_NONE;
02866                                 }
02867                         }
02868                         ent = &g_entities[entityList[e]];
02869                         if (ent && ent != self && ent->client)
02870                         {
02871                                 gotatleastone = qtrue;
02872                                 WP_AddAsMindtricked(&self->client->ps.fd, ent->s.number);
02873                         }
02874                         e++;
02875                 }
02876 
02877                 if (gotatleastone)
02878                 {
02879                         self->client->ps.forceAllowDeactivateTime = level.time + 1500;
02880 
02881                         if ( !tookPower )
02882                         {
02883                                 WP_ForcePowerStart( self, FP_TELEPATHY, 0 );
02884                         }
02885 
02886                         G_Sound( self, CHAN_AUTO, G_SoundIndex("sound/weapons/force/distract.wav") );
02887 
02888                         self->client->ps.forceHandExtend = HANDEXTEND_FORCEPUSH;
02889                         self->client->ps.forceHandExtendTime = level.time + 1000;
02890                 }
02891         }
02892 
02893 }
02894 
02895 void GEntity_UseFunc( gentity_t *self, gentity_t *other, gentity_t *activator )
02896 {
02897         GlobalUse(self, other, activator);
02898 }
02899 
02900 qboolean CanCounterThrow(gentity_t *self, gentity_t *thrower, qboolean pull)
02901 {
02902         int powerUse = 0;
02903 
02904         if (self->client->ps.forceHandExtend != HANDEXTEND_NONE)
02905         {
02906                 return 0;
02907         }
02908 
02909         if (self->client->ps.weaponTime > 0)
02910         {
02911                 return 0;
02912         }
02913 
02914         if ( self->health <= 0 )
02915         {
02916                 return 0;
02917         }
02918 
02919         if ( self->client->ps.powerups[PW_DISINT_4] > level.time )
02920         {
02921                 return 0;
02922         }
02923 
02924         if (self->client->ps.weaponstate == WEAPON_CHARGING ||
02925                 self->client->ps.weaponstate == WEAPON_CHARGING_ALT)
02926         { //don't autodefend when charging a weapon
02927                 return 0;
02928         }
02929 
02930         if (g_gametype.integer == GT_SIEGE &&
02931                 pull &&
02932                 thrower && thrower->client)
02933         { //in siege, pull will affect people if they are not facing you, so they can't run away so much
02934                 vec3_t d;
02935                 float a;
02936 
02937         VectorSubtract(thrower->client->ps.origin, self->client->ps.origin, d);
02938                 vectoangles(d, d);
02939 
02940         a = AngleSubtract(d[YAW], self->client->ps.viewangles[YAW]);
02941 
02942                 if (a > 60.0f || a < -60.0f)
02943                 { //if facing more than 60 degrees away they cannot defend
02944                         return 0;
02945                 }
02946         }
02947 
02948         if (pull)
02949         {
02950                 powerUse = FP_PULL;
02951         }
02952         else
02953         {
02954                 powerUse = FP_PUSH;
02955         }
02956 
02957         if ( !WP_ForcePowerUsable( self, powerUse ) )
02958         {
02959                 return 0;
02960         }
02961 
02962         if (self->client->ps.groundEntityNum == ENTITYNUM_NONE)
02963         { //you cannot counter a push/pull if you're in the air
02964                 return 0;
02965         }
02966 
02967         return 1;
02968 }
02969 
02970 qboolean G_InGetUpAnim(playerState_t *ps)
02971 {
02972         switch( (ps->legsAnim) )
02973         {
02974         case BOTH_GETUP1:
02975         case BOTH_GETUP2:
02976         case BOTH_GETUP3:
02977         case BOTH_GETUP4:
02978         case BOTH_GETUP5:
02979         case BOTH_FORCE_GETUP_F1:
02980         case BOTH_FORCE_GETUP_F2:
02981         case BOTH_FORCE_GETUP_B1:
02982         case BOTH_FORCE_GETUP_B2:
02983         case BOTH_FORCE_GETUP_B3:
02984         case BOTH_FORCE_GETUP_B4:
02985         case BOTH_FORCE_GETUP_B5:
02986         case BOTH_GETUP_BROLL_B:
02987         case BOTH_GETUP_BROLL_F:
02988         case BOTH_GETUP_BROLL_L:
02989         case BOTH_GETUP_BROLL_R:
02990         case BOTH_GETUP_FROLL_B:
02991         case BOTH_GETUP_FROLL_F:
02992         case BOTH_GETUP_FROLL_L:
02993         case BOTH_GETUP_FROLL_R:
02994                 return qtrue;
02995         }
02996 
02997         switch( (ps->torsoAnim) )
02998         {
02999         case BOTH_GETUP1:
03000         case BOTH_GETUP2:
03001         case BOTH_GETUP3:
03002         case BOTH_GETUP4:
03003         case BOTH_GETUP5:
03004         case BOTH_FORCE_GETUP_F1:
03005         case BOTH_FORCE_GETUP_F2:
03006         case BOTH_FORCE_GETUP_B1:
03007         case BOTH_FORCE_GETUP_B2:
03008         case BOTH_FORCE_GETUP_B3:
03009         case BOTH_FORCE_GETUP_B4:
03010         case BOTH_FORCE_GETUP_B5:
03011         case BOTH_GETUP_BROLL_B:
03012         case BOTH_GETUP_BROLL_F:
03013         case BOTH_GETUP_BROLL_L:
03014         case BOTH_GETUP_BROLL_R:
03015         case BOTH_GETUP_FROLL_B:
03016         case BOTH_GETUP_FROLL_F:
03017         case BOTH_GETUP_FROLL_L:
03018         case BOTH_GETUP_FROLL_R:
03019                 return qtrue;
03020         }
03021 
03022         return qfalse;
03023 }
03024 
03025 void G_LetGoOfWall( gentity_t *ent )
03026 {
03027         if ( !ent || !ent->client )
03028         {
03029                 return;
03030         }
03031         ent->client->ps.pm_flags &= ~PMF_STUCK_TO_WALL;
03032         if ( BG_InReboundJump( ent->client->ps.legsAnim ) 
03033                 || BG_InReboundHold( ent->client->ps.legsAnim ) )
03034         {
03035                 ent->client->ps.legsTimer = 0;
03036         }
03037         if ( BG_InReboundJump( ent->client->ps.torsoAnim ) 
03038                 || BG_InReboundHold( ent->client->ps.torsoAnim ) )
03039         {
03040                 ent->client->ps.torsoTimer = 0;
03041         }
03042 }
03043 
03044 float forcePushPullRadius[NUM_FORCE_POWER_LEVELS] =
03045 {
03046         0,//none
03047         384,//256,
03048         448,//384,
03049         512
03050 };
03051 //rwwFIXMEFIXME: incorporate this into the below function? Currently it's only being used by jedi AI
03052 
03053 extern void Touch_Button(gentity_t *ent, gentity_t *other, trace_t *trace );
03054 void ForceThrow( gentity_t *self, qboolean pull )
03055 {
03056         //shove things in front of you away
03057         float           dist;
03058         gentity_t       *ent;
03059         int                     entityList[MAX_GENTITIES];
03060         gentity_t       *push_list[MAX_GENTITIES];
03061         int                     numListedEntities;
03062         vec3_t          mins, maxs;
03063         vec3_t          v;
03064         int                     i, e;
03065         int                     ent_count = 0;
03066         int                     radius = 1024; //since it's view-based now. //350;
03067         int                     powerLevel;
03068         int                     visionArc;
03069         int                     pushPower;
03070         int                     pushPowerMod;
03071         vec3_t          center, ent_org, size, forward, right, end, dir, fwdangles = {0};
03072         float           dot1;
03073         trace_t         tr;
03074         int                     x;
03075         vec3_t          pushDir;
03076         vec3_t          thispush_org;
03077         vec3_t          tfrom, tto, fwd, a;
03078         float           knockback = pull?0:200;
03079         int                     powerUse = 0;
03080 
03081         visionArc = 0;
03082 
03083         if (self->client->ps.forceHandExtend != HANDEXTEND_NONE && (self->client->ps.forceHandExtend != HANDEXTEND_KNOCKDOWN || !G_InGetUpAnim(&self->client->ps)))
03084         {
03085                 return;
03086         }
03087 
03088         if (!g_useWhileThrowing.integer && self->client->ps.saberInFlight)
03089         {
03090                 return;
03091         }
03092 
03093         if (self->client->ps.weaponTime > 0)
03094         {
03095                 return;
03096         }
03097 
03098         if ( self->health <= 0 )
03099         {
03100                 return;
03101         }
03102         if ( self->client->ps.powerups[PW_DISINT_4] > level.time )
03103         {
03104                 return;
03105         }
03106         if (pull)
03107         {
03108                 powerUse = FP_PULL;
03109         }
03110         else
03111         {
03112                 powerUse = FP_PUSH;
03113         }
03114 
03115         if ( !WP_ForcePowerUsable( self, powerUse ) )
03116         {
03117                 return;
03118         }
03119 
03120         if (!pull && self->client->ps.saberLockTime > level.time && self->client->ps.saberLockFrame)
03121         {
03122                 G_Sound( self, CHAN_BODY, G_SoundIndex( "sound/weapons/force/push.wav" ) );
03123                 self->client->ps.powerups[PW_DISINT_4] = level.time + 1500;
03124 
03125                 self->client->ps.saberLockHits += self->client->ps.fd.forcePowerLevel[FP_PUSH]*2;
03126 
03127                 WP_ForcePowerStart( self, FP_PUSH, 0 );
03128                 return;
03129         }
03130 
03131         WP_ForcePowerStart( self, powerUse, 0 );
03132 
03133         //make sure this plays and that you cannot press fire for about 1 second after this
03134         if ( pull )
03135         {
03136                 G_Sound( self, CHAN_BODY, G_SoundIndex( "sound/weapons/force/pull.wav" ) );
03137                 if (self->client->ps.forceHandExtend == HANDEXTEND_NONE)
03138                 {
03139                         self->client->ps.forceHandExtend = HANDEXTEND_FORCEPULL;
03140                         if ( g_gametype.integer == GT_SIEGE && self->client->ps.weapon == WP_SABER )
03141                         {//hold less so can attack right after a pull
03142                                 self->client->ps.forceHandExtendTime = level.time + 200;
03143                         }
03144                         else
03145                         {
03146                                 self->client->ps.forceHandExtendTime = level.time + 400;
03147                         }
03148                 }
03149                 self->client->ps.powerups[PW_DISINT_4] = self->client->ps.forceHandExtendTime + 200;
03150                 self->client->ps.powerups[PW_PULL] = self->client->ps.powerups[PW_DISINT_4];
03151         }
03152         else
03153         {
03154                 G_Sound( self, CHAN_BODY, G_SoundIndex( "sound/weapons/force/push.wav" ) );
03155                 if (self->client->ps.forceHandExtend == HANDEXTEND_NONE)
03156                 {
03157                         self->client->ps.forceHandExtend = HANDEXTEND_FORCEPUSH;
03158                         self->client->ps.forceHandExtendTime = level.time + 1000;
03159                 }
03160                 else if (self->client->ps.forceHandExtend == HANDEXTEND_KNOCKDOWN && G_InGetUpAnim(&self->client->ps))
03161                 {
03162                         if (self->client->ps.forceDodgeAnim > 4)
03163                         {
03164                                 self->client->ps.forceDodgeAnim -= 8;
03165                         }
03166                         self->client->ps.forceDodgeAnim += 8; //special case, play push on upper torso, but keep playing current knockdown anim on legs
03167                 }
03168                 self->client->ps.powerups[PW_DISINT_4] = level.time + 1100;
03169                 self->client->ps.powerups[PW_PULL] = 0;
03170         }
03171 
03172         VectorCopy( self->client->ps.viewangles, fwdangles );
03173         AngleVectors( fwdangles, forward, right, NULL );
03174         VectorCopy( self->client->ps.origin, center );
03175 
03176         for ( i = 0 ; i < 3 ; i++ ) 
03177         {
03178                 mins[i] = center[i] - radius;
03179                 maxs[i] = center[i] + radius;
03180         }
03181 
03182 
03183         if (pull)
03184         {
03185                 powerLevel = self->client->ps.fd.forcePowerLevel[FP_PULL];
03186                 pushPower = 256*self->client->ps.fd.forcePowerLevel[FP_PULL];
03187         }
03188         else
03189         {
03190                 powerLevel = self->client->ps.fd.forcePowerLevel[FP_PUSH];
03191                 pushPower = 256*self->client->ps.fd.forcePowerLevel[FP_PUSH];
03192         }
03193 
03194         if (!powerLevel)
03195         { //Shouldn't have made it here..
03196                 return;
03197         }
03198 
03199         if (powerLevel == FORCE_LEVEL_2)
03200         {
03201                 visionArc = 60;
03202         }
03203         else if (powerLevel == FORCE_LEVEL_3)
03204         {
03205                 visionArc = 180;
03206         }
03207 
03208         if (powerLevel == FORCE_LEVEL_1)
03209         { //can only push/pull targeted things at level 1
03210                 VectorCopy(self->client->ps.origin, tfrom);
03211                 tfrom[2] += self->client->ps.viewheight;
03212                 AngleVectors(self->client->ps.viewangles, fwd, NULL, NULL);
03213                 tto[0] = tfrom[0] + fwd[0]*radius/2;
03214                 tto[1] = tfrom[1] + fwd[1]*radius/2;
03215                 tto[2] = tfrom[2] + fwd[2]*radius/2;
03216 
03217                 trap_Trace(&tr, tfrom, NULL, NULL, tto, self->s.number, MASK_PLAYERSOLID);
03218 
03219                 if (tr.fraction != 1.0 &&
03220                         tr.entityNum != ENTITYNUM_NONE)
03221                 {
03222                         if (!g_entities[tr.entityNum].client && g_entities[tr.entityNum].s.eType == ET_NPC)
03223                         { //g2animent
03224                                 if (g_entities[tr.entityNum].s.genericenemyindex < level.time)
03225                                 {
03226                                         g_entities[tr.entityNum].s.genericenemyindex = level.time + 2000;
03227                                 }
03228                         }
03229 
03230                         numListedEntities = 0;
03231                         entityList[numListedEntities] = tr.entityNum;
03232 
03233                         if (pull)
03234                         {
03235                                 if (!ForcePowerUsableOn(self, &g_entities[tr.entityNum], FP_PULL))
03236                                 {
03237                                         return;
03238                                 }
03239                         }
03240                         else
03241                         {
03242                                 if (!ForcePowerUsableOn(self, &g_entities[tr.entityNum], FP_PUSH))
03243                                 {
03244                                         return;
03245                                 }
03246                         }
03247                         numListedEntities++;
03248                 }
03249                 else
03250                 {
03251                         //didn't get anything, so just
03252                         return;
03253                 }
03254         }
03255         else
03256         {
03257                 numListedEntities = trap_EntitiesInBox( mins, maxs, entityList, MAX_GENTITIES );
03258 
03259                 e = 0;
03260 
03261                 while (e < numListedEntities)
03262                 {
03263                         ent = &g_entities[entityList[e]];
03264 
03265                         if (!ent->client && ent->s.eType == ET_NPC)
03266                         { //g2animent
03267                                 if (ent->s.genericenemyindex < level.time)
03268                                 {
03269                                         ent->s.genericenemyindex = level.time + 2000;
03270                                 }
03271                         }
03272 
03273                         if (ent)
03274                         {
03275                                 if (ent->client)
03276                                 {
03277                                         VectorCopy(ent->client->ps.origin, thispush_org);
03278                                 }
03279                                 else
03280                                 {
03281                                         VectorCopy(ent->s.pos.trBase, thispush_org);
03282                                 }
03283                         }
03284 
03285                         if (ent)
03286                         { //not in the arc, don't consider it
03287                                 VectorCopy(self->client->ps.origin, tto);
03288                                 tto[2] += self->client->ps.viewheight;
03289                                 VectorSubtract(thispush_org, tto, a);
03290                                 vectoangles(a, a);
03291 
03292                                 if (ent->client && !InFieldOfVision(self->client->ps.viewangles, visionArc, a) &&
03293                                         ForcePowerUsableOn(self, ent, powerUse))
03294                                 { //only bother with arc rules if the victim is a client
03295                                         entityList[e] = ENTITYNUM_NONE;
03296                                 }
03297                                 else if (ent->client)
03298                                 {
03299                                         if (pull)
03300                                         {
03301                                                 if (!ForcePowerUsableOn(self, ent, FP_PULL))
03302                                                 {
03303                                                         entityList[e] = ENTITYNUM_NONE;
03304                                                 }
03305                                         }
03306                                         else
03307                                         {
03308                                                 if (!ForcePowerUsableOn(self, ent, FP_PUSH))
03309                                                 {
03310                                                         entityList[e] = ENTITYNUM_NONE;
03311                                                 }
03312                                         }
03313                                 }
03314                         }
03315                         e++;
03316                 }
03317         }
03318 
03319         for ( e = 0 ; e < numListedEntities ; e++ ) 
03320         {
03321                 if (entityList[e] != ENTITYNUM_NONE &&
03322                         entityList[e] >= 0 &&
03323                         entityList[e] < MAX_GENTITIES)
03324                 {
03325                         ent = &g_entities[entityList[e]];
03326                 }
03327                 else
03328                 {
03329                         ent = NULL;
03330                 }
03331 
03332                 if (!ent)
03333                         continue;
03334                 if (ent == self)
03335                         continue;
03336                 if (ent->client && OnSameTeam(ent, self))
03337                 {
03338                         continue;
03339                 }
03340                 if ( !(ent->inuse) )
03341                         continue;
03342                 if ( ent->s.eType != ET_MISSILE )
03343                 {
03344                         if ( ent->s.eType != ET_ITEM )
03345                         {
03346                                 //FIXME: need pushable objects
03347                                 if ( Q_stricmp( "func_button", ent->classname ) == 0 )
03348                                 {//we might push it
03349                                         if ( pull || !(ent->spawnflags&SPF_BUTTON_FPUSHABLE) )
03350                                         {//not force-pushable, never pullable
03351                                                 continue;
03352                                         }
03353                                 }
03354                                 else
03355                                 {
03356                                         if ( ent->s.eFlags & EF_NODRAW )
03357                                         {
03358                                                 continue;
03359                                         }
03360                                         if ( !ent->client )
03361                                         {
03362                                                 if ( Q_stricmp( "lightsaber", ent->classname ) != 0 )
03363                                                 {//not a lightsaber 
03364                                                         if ( Q_stricmp( "func_door", ent->classname ) != 0 || !(ent->spawnflags & 2/*MOVER_FORCE_ACTIVATE*/) )
03365                                                         {//not a force-usable door
03366                                                                 if ( Q_stricmp( "func_static", ent->classname ) != 0 || (!(ent->spawnflags&1/*F_PUSH*/)&&!(ent->spawnflags&2/*F_PULL*/)) )
03367                                                                 {//not a force-usable func_static
03368                                                                         if ( Q_stricmp( "limb", ent->classname ) )
03369                                                                         {//not a limb
03370                                                                                 continue;
03371                                                                         }
03372                                                                 }
03373                                                         }
03374                                                         else if ( ent->moverState != MOVER_POS1 && ent->moverState != MOVER_POS2 )
03375                                                         {//not at rest
03376                                                                 continue;
03377                                                         }
03378                                                 }
03379                                         }
03380                                         else if ( ent->client->NPC_class == CLASS_GALAKMECH 
03381                                                 || ent->client->NPC_class == CLASS_ATST
03382                                                 || ent->client->NPC_class == CLASS_RANCOR )
03383                                         {//can't push ATST or Galak or Rancor
03384                                                 continue;
03385                                         }
03386                                 }
03387                         }
03388                 }
03389                 else
03390                 {
03391                         if ( ent->s.pos.trType == TR_STATIONARY && (ent->s.eFlags&EF_MISSILE_STICK) )
03392                         {//can't force-push/pull stuck missiles (detpacks, tripmines)
03393                                 continue;
03394                         }
03395                         if ( ent->s.pos.trType == TR_STATIONARY && ent->s.weapon != WP_THERMAL )
03396                         {//only thermal detonators can be pushed once stopped
03397                                 continue;
03398                         }
03399                 }
03400 
03401                 //this is all to see if we need to start a saber attack, if it's in flight, this doesn't matter
03402                 // find the distance from the edge of the bounding box
03403                 for ( i = 0 ; i < 3 ; i++ ) 
03404                 {
03405                         if ( center[i] < ent->r.absmin[i] ) 
03406                         {
03407                                 v[i] = ent->r.absmin[i] - center[i];
03408                         } else if ( center[i] > ent->r.absmax[i] ) 
03409                         {
03410                                 v[i] = center[i] - ent->r.absmax[i];
03411                         } else 
03412                         {
03413                                 v[i] = 0;
03414                         }
03415                 }
03416 
03417                 VectorSubtract( ent->r.absmax, ent->r.absmin, size );
03418                 VectorMA( ent->r.absmin, 0.5, size, ent_org );
03419 
03420                 VectorSubtract( ent_org, center, dir );
03421                 VectorNormalize( dir );
03422                 if ( (dot1 = DotProduct( dir, forward )) < 0.6 )
03423                         continue;
03424 
03425                 dist = VectorLength( v );
03426 
03427                 //Now check and see if we can actually deflect it
03428                 //method1
03429                 //if within a certain range, deflect it
03430                 if ( dist >= radius ) 
03431                 {
03432                         continue;
03433                 }
03434         
03435                 //in PVS?
03436                 if ( !ent->r.bmodel && !trap_InPVS( ent_org, self->client->ps.origin ) )
03437                 {//must be in PVS
03438                         continue;
03439                 }
03440 
03441                 //really should have a clear LOS to this thing...
03442                 trap_Trace( &tr, self->client->ps.origin, vec3_origin, vec3_origin, ent_org, self->s.number, MASK_SHOT );
03443                 if ( tr.fraction < 1.0f && tr.entityNum != ent->s.number )
03444                 {//must have clear LOS
03445                         //try from eyes too before you give up
03446                         vec3_t eyePoint;
03447                         VectorCopy(self->client->ps.origin, eyePoint);
03448                         eyePoint[2] += self->client->ps.viewheight;
03449                         trap_Trace( &tr, eyePoint, vec3_origin, vec3_origin, ent_org, self->s.number, MASK_SHOT );
03450 
03451                         if ( tr.fraction < 1.0f && tr.entityNum != ent->s.number )
03452                         {
03453                                 continue;
03454                         }
03455                 }
03456 
03457                 // ok, we are within the radius, add us to the incoming list
03458                 push_list[ent_count] = ent;
03459                 ent_count++;
03460         }
03461 
03462         if ( ent_count )
03463         {
03464                 //method1:
03465                 for ( x = 0; x < ent_count; x++ )
03466                 {
03467                         int modPowerLevel = powerLevel;
03468 
03469         
03470                         if (push_list[x]->client)
03471                         {
03472                                 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]);
03473                                 if (modPowerLevel == -1)
03474                                 {
03475                                         modPowerLevel = powerLevel;
03476                                 }
03477                         }
03478 
03479                         pushPower = 256*modPowerLevel;
03480 
03481                         if (push_list[x]->client)
03482                         {
03483                                 VectorCopy(push_list[x]->client->ps.origin, thispush_org);
03484                         }
03485                         else
03486                         {
03487                                 VectorCopy(push_list[x]->s.origin, thispush_org);
03488                         }
03489 
03490                         if ( push_list[x]->client )
03491                         {//FIXME: make enemy jedi able to hunker down and resist this?
03492                                 int otherPushPower = push_list[x]->client->ps.fd.forcePowerLevel[powerUse];
03493                                 qboolean canPullWeapon = qtrue;
03494                                 float dirLen = 0;
03495 
03496                                 if ( g_debugMelee.integer )
03497                                 {
03498                                         if ( (push_list[x]->client->ps.pm_flags&PMF_STUCK_TO_WALL) )
03499                                         {//no resistance if stuck to wall
03500                                                 //push/pull them off the wall
03501                                                 otherPushPower = 0;
03502                                                 G_LetGoOfWall( push_list[x] );
03503                                         }
03504                                 }
03505 
03506                                 knockback = pull?0:200;
03507 
03508                                 pushPowerMod = pushPower;
03509 
03510                                 if (push_list[x]->client->pers.cmd.forwardmove ||
03511                                         push_list[x]->client->pers.cmd.rightmove)
03512                                 { //if you are moving, you get one less level of defense
03513                                         otherPushPower--;
03514 
03515                                         if (otherPushPower < 0)
03516                                         {
03517                                                 otherPushPower = 0;
03518                                         }
03519                                 }
03520 
03521                                 if (otherPushPower && CanCounterThrow(push_list[x], self, pull))
03522                                 {
03523                                         if ( pull )
03524                                         {
03525                                                 G_Sound( push_list[x], CHAN_BODY, G_SoundIndex( "sound/weapons/force/pull.wav" ) );
03526                                                 push_list[x]->client->ps.forceHandExtend = HANDEXTEND_FORCEPULL;
03527                                                 push_list[x]->client->ps.forceHandExtendTime = level.time + 400;
03528                                         }
03529                                         else
03530                                         {
03531                                                 G_Sound( push_list[x], CHAN_BODY, G_SoundIndex( "sound/weapons/force/push.wav" ) );
03532                                                 push_list[x]->client->ps.forceHandExtend = HANDEXTEND_FORCEPUSH;
03533                                                 push_list[x]->client->ps.forceHandExtendTime = level.time + 1000;
03534                                         }
03535                                         push_list[x]->client->ps.powerups[PW_DISINT_4] = push_list[x]->client->ps.forceHandExtendTime + 200;
03536 
03537                                         if (pull)
03538                                         {
03539                                                 push_list[x]->client->ps.powerups[PW_PULL] = push_list[x]->client->ps.powerups[PW_DISINT_4];
03540                                         }
03541                                         else
03542                                         {
03543                                                 push_list[x]->client->ps.powerups[PW_PULL] = 0;
03544                                         }
03545 
03546                                         //Make a counter-throw effect
03547 
03548                                         if (otherPushPower >= modPowerLevel)
03549                                         {
03550                                                 pushPowerMod = 0;
03551                                                 canPullWeapon = qfalse;
03552                                         }
03553                                         else
03554                                         {
03555                                                 int powerDif = (modPowerLevel - otherPushPower);
03556 
03557                                                 if (powerDif >= 3)
03558                                                 {
03559                                                         pushPowerMod -= pushPowerMod*0.2;
03560                                                 }
03561                                                 else if (powerDif == 2)
03562                                                 {
03563                                                         pushPowerMod -= pushPowerMod*0.4;
03564                                                 }
03565                                                 else if (powerDif == 1)
03566                                                 {
03567                                                         pushPowerMod -= pushPowerMod*0.8;
03568                                                 }
03569 
03570                                                 if (pushPowerMod < 0)
03571                                                 {
03572                                                         pushPowerMod = 0;
03573                                                 }
03574                                         }
03575                                 }
03576 
03577                                 //shove them
03578                                 if ( pull )
03579                                 {
03580                                         VectorSubtract( self->client->ps.origin, thispush_org, pushDir );
03581 
03582                                         if (push_list[x]->client && VectorLength(pushDir) <= 256)
03583                                         {
03584                                                 int randfact = 0;
03585 
03586                                                 if (modPowerLevel == FORCE_LEVEL_1)
03587                                                 {
03588                                                         randfact = 3;
03589                                                 }
03590                                                 else if (modPowerLevel == FORCE_LEVEL_2)
03591                                                 {
03592                                                         randfact = 7;
03593                                                 }
03594                                                 else if (modPowerLevel == FORCE_LEVEL_3)
03595                                                 {
03596                                                         randfact = 10;
03597                                                 }
03598 
03599                                                 if (!OnSameTeam(self, push_list[x]) && Q_irand(1, 10) <= randfact && canPullWeapon)
03600                                                 {
03601                                                         vec3_t uorg, vecnorm;
03602 
03603                                                         VectorCopy(self->client->ps.origin, uorg);
03604                                                         uorg[2] += 64;
03605 
03606                                                         VectorSubtract(uorg, thispush_org, vecnorm);
03607                                                         VectorNormalize(vecnorm);
03608 
03609                                                         TossClientWeapon(push_list[x], vecnorm, 500);
03610                                                 }
03611                                         }
03612                                 }
03613                                 else
03614                                 {
03615                                         VectorSubtract( thispush_org, self->client->ps.origin, pushDir );
03616                                 }
03617 
03618                                 if ((modPowerLevel > otherPushPower || push_list[x]->client->ps.m_iVehicleNum) && push_list[x]->client)
03619                                 {
03620                                         if (modPowerLevel == FORCE_LEVEL_3 &&
03621                                                 push_list[x]->client->ps.forceHandExtend != HANDEXTEND_KNOCKDOWN)
03622                                         {
03623                                                 dirLen = VectorLength(pushDir);
03624 
03625                                                 if (BG_KnockDownable(&push_list[x]->client->ps) &&
03626                                                         dirLen <= (64*((modPowerLevel - otherPushPower)-1)))
03627                                                 { //can only do a knockdown if fairly close
03628                                                         push_list[x]->client->ps.forceHandExtend = HANDEXTEND_KNOCKDOWN;
03629                                                         push_list[x]->client->ps.forceHandExtendTime = level.time + 700;
03630                                                         push_list[x]->client->ps.forceDodgeAnim = 0; //this toggles between 1 and 0, when it's 1 we should play the get up anim
03631                                                         push_list[x]->client->ps.quickerGetup = qtrue;
03632                                                 }
03633                                                 else if (push_list[x]->s.number < MAX_CLIENTS && push_list[x]->client->ps.m_iVehicleNum &&
03634                                                         dirLen <= 128.0f )
03635                                                 { //a player on a vehicle
03636                                                         gentity_t *vehEnt = &g_entities[push_list[x]->client->ps.m_iVehicleNum];
03637                                                         if (vehEnt->inuse && vehEnt->client && vehEnt->m_pVehicle)
03638                                                         {
03639                                                                 if (vehEnt->m_pVehicle->m_pVehicleInfo->type == VH_SPEEDER ||
03640                                                                         vehEnt->m_pVehicle->m_pVehicleInfo->type == VH_ANIMAL)
03641                                                                 { //push the guy off
03642                                                                         vehEnt->m_pVehicle->m_pVehicleInfo->Eject(vehEnt->m_pVehicle, (bgEntity_t *)push_list[x], qfalse);
03643                                                                 }
03644                                                         }
03645                                                 }
03646                                         }
03647                                 }
03648 
03649                                 if (!dirLen)
03650                                 {
03651                                         dirLen = VectorLength(pushDir);
03652                                 }
03653 
03654                                 VectorNormalize(pushDir);
03655 
03656                                 if (push_list[x]->client)
03657                                 {
03658                                         //escape a force grip if we're in one
03659                                         if (self->client->ps.fd.forceGripBeingGripped > level.time)
03660                                         { //force the enemy to stop gripping me if I managed to push him
03661                                                 if (push_list[x]->client->ps.fd.forceGripEntityNum == self->s.number)
03662                                                 {
03663                                                         if (modPowerLevel >= push_list[x]->client->ps.fd.forcePowerLevel[FP_GRIP])
03664                                                         { //only break the grip if our push/pull level is >= their grip level
03665                                                                 WP_ForcePowerStop(push_list[x], FP_GRIP);
03666                                                                 self->client->ps.fd.forceGripBeingGripped = 0;
03667                                                                 push_list[x]->client->ps.fd.forceGripUseTime = level.time + 1000; //since we just broke out of it..
03668                                                         }
03669                                                 }
03670                                         }
03671 
03672                                         push_list[x]->client->ps.otherKiller = self->s.number;
03673                                         push_list[x]->client->ps.otherKillerTime = level.time + 5000;
03674                                         push_list[x]->client->ps.otherKillerDebounceTime = level.time + 100;
03675 
03676                                         pushPowerMod -= (dirLen*0.7);
03677                                         if (pushPowerMod < 16)
03678                                         {
03679                                                 pushPowerMod = 16;
03680                                         }
03681 
03682                                         //fullbody push effect
03683                                         push_list[x]->client->pushEffectTime = level.time + 600;
03684 
03685                                         push_list[x]->client->ps.velocity[0] = pushDir[0]*pushPowerMod;
03686                                         push_list[x]->client->ps.velocity[1] = pushDir[1]*pushPowerMod;
03687 
03688                                         if ((int)push_list[x]->client->ps.velocity[2] == 0)
03689                                         { //if not going anywhere vertically, boost them up a bit
03690                                                 push_list[x]->client->ps.velocity[2] = pushDir[2]*pushPowerMod;
03691 
03692                                                 if (push_list[x]->client->ps.velocity[2] < 128)
03693                                                 {
03694                                                         push_list[x]->client->ps.velocity[2] = 128;
03695                                                 }
03696                                         }
03697                                         else
03698                                         {
03699                                                 push_list[x]->client->ps.velocity[2] = pushDir[2]*pushPowerMod;
03700                                         }
03701                                 }
03702                         }
03703                         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
03704                         {
03705                                 if ( pull )
03706                                 {//deflect rather than reflect?
03707                                 }
03708                                 else 
03709                                 {
03710                                         G_ReflectMissile( self, push_list[x], forward );
03711                                 }
03712                         }
03713                         else if ( !Q_stricmp( "func_static", push_list[x]->classname ) )
03714                         {//force-usable func_static
03715                                 if ( !pull && (push_list[x]->spawnflags&1/*F_PUSH*/) )
03716                                 {
03717                                         GEntity_UseFunc( push_list[x], self, self );
03718                                 }
03719                                 else if ( pull && (push_list[x]->spawnflags&2/*F_PULL*/) )
03720                                 {
03721                                         GEntity_UseFunc( push_list[x], self, self );
03722                                 }
03723                         }
03724                         else if ( !Q_stricmp( "func_door", push_list[x]->classname ) && (push_list[x]->spawnflags&2) )
03725                         {//push/pull the door
03726                                 vec3_t  pos1, pos2;
03727                                 vec3_t  trFrom;
03728 
03729                                 VectorCopy(self->client->ps.origin, trFrom);
03730                                 trFrom[2] += self->client->ps.viewheight;
03731 
03732                                 AngleVectors( self->client->ps.viewangles, forward, NULL, NULL );
03733                                 VectorNormalize( forward );
03734                                 VectorMA( trFrom, radius, forward, end );
03735                                 trap_Trace( &tr, trFrom, vec3_origin, vec3_origin, end, self->s.number, MASK_SHOT );
03736                                 if ( tr.entityNum != push_list[x]->s.number || tr.fraction == 1.0 || tr.allsolid || tr.startsolid )
03737                                 {//must be pointing right at it
03738                                         continue;
03739                                 }
03740 
03741                                 if ( VectorCompare( vec3_origin, push_list[x]->s.origin ) )
03742                                 {//does not have an origin brush, so pos1 & pos2 are relative to world origin, need to calc center
03743                                         VectorSubtract( push_list[x]->r.absmax, push_list[x]->r.absmin, size );
03744                                         VectorMA( push_list[x]->r.absmin, 0.5, size, center );
03745                                         if ( (push_list[x]->spawnflags&1) && push_list[x]->moverState == MOVER_POS1 )
03746                                         {//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
03747                                                 VectorSubtract( center, push_list[x]->pos1, center );
03748                                         }
03749                                         else if ( !(push_list[x]->spawnflags&1) && push_list[x]->moverState == MOVER_POS2 )
03750                                         {//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
03751                                                 VectorSubtract( center, push_list[x]->pos2, center );
03752                                         }
03753                                         VectorAdd( center, push_list[x]->pos1, pos1 );
03754                                         VectorAdd( center, push_list[x]->pos2, pos2 );
03755                                 }
03756                                 else
03757                                 {//actually has an origin, pos1 and pos2 are absolute
03758                                         VectorCopy( push_list[x]->r.currentOrigin, center );
03759                                         VectorCopy( push_list[x]->pos1, pos1 );
03760                                         VectorCopy( push_list[x]->pos2, pos2 );
03761                                 }
03762 
03763                                 if ( Distance( pos1, trFrom ) < Distance( pos2, trFrom ) )
03764                                 {//pos1 is closer
03765                                         if ( push_list[x]->moverState == MOVER_POS1 )
03766                                         {//at the closest pos
03767                                                 if ( pull )
03768                                                 {//trying to pull, but already at closest point, so screw it
03769                                                         continue;
03770                                                 }
03771                                         }
03772                                         else if ( push_list[x]->moverState == MOVER_POS2 )
03773                                         {//at farthest pos
03774                                                 if ( !pull )
03775                                                 {//trying to push, but already at farthest point, so screw it
03776                                                         continue;
03777                                                 }
03778                                         }
03779                                 }
03780                                 else
03781                                 {//pos2 is closer
03782                                         if ( push_list[x]->moverState == MOVER_POS1 )
03783                                         {//at the farthest pos
03784                                                 if ( !pull )
03785                                                 {//trying to push, but already at farthest point, so screw it
03786                                                         continue;
03787                                                 }
03788                                         }
03789                                         else if ( push_list[x]->moverState == MOVER_POS2 )
03790                                         {//at closest pos
03791                                                 if ( pull )
03792                                                 {//trying to pull, but already at closest point, so screw it
03793                                                         continue;
03794                                                 }
03795                                         }
03796                                 }
03797                                 GEntity_UseFunc( push_list[x], self, self );
03798                         }
03799                         else if ( Q_stricmp( "func_button", push_list[x]->classname ) == 0 )
03800                         {//pretend you pushed it
03801                                 Touch_Button( push_list[x], self, NULL );
03802                                 continue;
03803                         }
03804                 }
03805         }
03806 
03807         //attempt to break any leftover grips
03808         //if we're still in a current grip that wasn't broken by the push, it will still remain
03809         self->client->dangerTime = level.time;
03810         self->client->ps.eFlags &= ~EF_INVULNERABLE;
03811         self->client->invulnerableTimer = 0;
03812 
03813         if (self->client->ps.fd.forceGripBeingGripped > level.time)
03814         {
03815                 self->client->ps.fd.forceGripBeingGripped = 0;
03816         }
03817 }
03818 
03819 void WP_ForcePowerStop( gentity_t *self, forcePowers_t forcePower )
03820 {
03821         int wasActive = self->client->ps.fd.forcePowersActive;
03822 
03823         self->client->ps.fd.forcePowersActive &= ~( 1 << forcePower );
03824 
03825         switch( (int)forcePower )
03826         {
03827         case FP_HEAL:
03828                 self->client->ps.fd.forceHealAmount = 0;
03829                 self->client->ps.fd.forceHealTime = 0;
03830                 break;
03831         case FP_LEVITATION:
03832                 break;
03833         case FP_SPEED:
03834                 if (wasActive & (1 << FP_SPEED))
03835                 {
03836                         G_MuteSound(self->client->ps.fd.killSoundEntIndex[TRACK_CHANNEL_2-50], CHAN_VOICE);
03837                 }
03838                 break;
03839         case FP_PUSH:
03840                 break;
03841         case FP_PULL:
03842                 break;
03843         case FP_TELEPATHY:
03844                 if (wasActive & (1 << FP_TELEPATHY))
03845                 {
03846                         G_Sound( self, CHAN_AUTO, G_SoundIndex("sound/weapons/force/distractstop.wav") );
03847                 }
03848                 self->client->ps.fd.forceMindtrickTargetIndex = 0;
03849                 self->client->ps.fd.forceMindtrickTargetIndex2 = 0;
03850                 self->client->ps.fd.forceMindtrickTargetIndex3 = 0;
03851                 self->client->ps.fd.forceMindtrickTargetIndex4 = 0;
03852                 break;
03853         case FP_SEE:
03854                 if (wasActive & (1 << FP_SEE))
03855                 {
03856                         G_MuteSound(self->client->ps.fd.killSoundEntIndex[TRACK_CHANNEL_5-50], CHAN_VOICE);
03857                 }
03858                 break;
03859         case FP_GRIP:
03860                 self->client->ps.fd.forceGripUseTime = level.time + 3000;
03861                 if (self->client->ps.fd.forcePowerLevel[FP_GRIP] > FORCE_LEVEL_1 &&
03862                         g_entities[self->client->ps.fd.forceGripEntityNum].client &&
03863                         g_entities[self->client->ps.fd.forceGripEntityNum].health > 0 &&
03864                         g_entities[self->client->ps.fd.forceGripEntityNum].inuse &&
03865                         (level.time - g_entities[self->client->ps.fd.forceGripEntityNum].client->ps.fd.forceGripStarted) > 500)
03866                 { //if we had our throat crushed in for more than half a second, gasp for air when we're let go
03867                         if (wasActive & (1 << FP_GRIP))
03868                         {
03869                                 G_EntitySound( &g_entities[self->client->ps.fd.forceGripEntityNum], CHAN_VOICE, G_SoundIndex("*gasp.wav") );
03870                         }
03871                 }
03872 
03873                 if (g_entities[self->client->ps.fd.forceGripEntityNum].client &&
03874                         g_entities[self->client->ps.fd.forceGripEntityNum].inuse)
03875                 {
03876                         
03877                         g_entities[self->client->ps.fd.forceGripEntityNum].client->ps.forceGripChangeMovetype = PM_NORMAL;
03878                 }
03879 
03880                 if (self->client->ps.forceHandExtend == HANDEXTEND_FORCE_HOLD)
03881                 {
03882                         self->client->ps.forceHandExtendTime = 0;
03883                 }
03884 
03885                 self->client->ps.fd.forceGripEntityNum = ENTITYNUM_NONE;
03886 
03887                 self->client->ps.powerups[PW_DISINT_4] = 0;
03888                 break;
03889         case FP_LIGHTNING:
03890                 if ( self->client->ps.fd.forcePowerLevel[FP_LIGHTNING] < FORCE_LEVEL_2 )
03891                 {//don't do it again for 3 seconds, minimum... FIXME: this should be automatic once regeneration is slower (normal)
03892                         self->client->ps.fd.forcePowerDebounce[FP_LIGHTNING] = level.time + 3000;
03893                 }
03894                 else
03895                 {
03896                         self->client->ps.fd.forcePowerDebounce[FP_LIGHTNING] = level.time + 1500;
03897                 }
03898                 if (self->client->ps.forceHandExtend == HANDEXTEND_FORCE_HOLD)
03899                 {
03900                         self->client->ps.forceHandExtendTime = 0; //reset hand position
03901                 }
03902 
03903                 self->client->ps.activeForcePass = 0;
03904                 break;
03905         case FP_RAGE:
03906                 self->client->ps.fd.forceRageRecoveryTime = level.time + 10000;
03907                 if (wasActive & (1 << FP_RAGE))
03908                 {
03909                         G_MuteSound(self->client->ps.fd.killSoundEntIndex[TRACK_CHANNEL_3-50], CHAN_VOICE);
03910                 }
03911                 break;
03912         case FP_ABSORB:
03913                 if (wasActive & (1 << FP_ABSORB))
03914                 {
03915                         G_MuteSound(self->client->ps.fd.killSoundEntIndex[TRACK_CHANNEL_3-50], CHAN_VOICE);
03916                 }
03917                 break;
03918         case FP_PROTECT:
03919                 if (wasActive & (1 << FP_PROTECT))
03920                 {
03921                         G_MuteSound(self->client->ps.fd.killSoundEntIndex[TRACK_CHANNEL_3-50], CHAN_VOICE);
03922                 }
03923                 break;
03924         case FP_DRAIN:
03925                 if ( self->client->ps.fd.forcePowerLevel[FP_DRAIN] < FORCE_LEVEL_2 )
03926                 {//don't do it again for 3 seconds, minimum...
03927                         self->client->ps.fd.forcePowerDebounce[FP_DRAIN] = level.time + 3000;
03928                 }
03929                 else
03930                 {
03931                         self->client->ps.fd.forcePowerDebounce[FP_DRAIN] = level.time + 1500;
03932                 }
03933 
03934                 if (self->client->ps.forceHandExtend == HANDEXTEND_FORCE_HOLD)
03935                 {
03936                         self->client->ps.forceHandExtendTime = 0; //reset hand position
03937                 }
03938 
03939                 self->client->ps.activeForcePass = 0;
03940         default:
03941                 break;
03942         }
03943 }
03944 
03945 void DoGripAction(gentity_t *self, forcePowers_t forcePower)
03946 {
03947         gentity_t *gripEnt;
03948         int gripLevel = 0;
03949         trace_t tr;
03950         vec3_t a;
03951         vec3_t fwd, fwd_o, start_o, nvel;
03952 
03953         self->client->dangerTime = level.time;
03954         self->client->ps.eFlags &= ~EF_INVULNERABLE;
03955         self->client->invulnerableTimer = 0;
03956 
03957         gripEnt = &g_entities[self->client->ps.fd.forceGripEntityNum];
03958 
03959         if (!gripEnt || !gripEnt->client || !gripEnt->inuse || gripEnt->health < 1 || !ForcePowerUsableOn(self, gripEnt, FP_GRIP))
03960         {
03961                 WP_ForcePowerStop(self, forcePower);
03962                 self->client->ps.fd.forceGripEntityNum = ENTITYNUM_NONE;
03963 
03964                 if (gripEnt && gripEnt->client && gripEnt->inuse)
03965                 {
03966                         gripEnt->client->ps.forceGripChangeMovetype = PM_NORMAL;
03967                 }
03968                 return;
03969         }
03970 
03971         VectorSubtract(gripEnt->client->ps.origin, self->client->ps.origin, a);
03972         
03973         trap_Trace(&tr, self->client->ps.origin, NULL, NULL, gripEnt->client->ps.origin, self->s.number, MASK_PLAYERSOLID);
03974 
03975         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]);
03976 
03977         if (gripLevel == -1)
03978         {
03979                 gripLevel = self->client->ps.fd.forcePowerLevel[FP_GRIP];
03980         }
03981 
03982         if (!gripLevel)
03983         {
03984                 WP_ForcePowerStop(self, forcePower);
03985                 return;
03986         }
03987 
03988         if (VectorLength(a) > MAX_GRIP_DISTANCE)
03989         {
03990                 WP_ForcePowerStop(self, forcePower);
03991                 return;
03992         }
03993 
03994         if ( !InFront( gripEnt->client->ps.origin, self->client->ps.origin, self->client->ps.viewangles, 0.9f ) &&
03995                 gripLevel < FORCE_LEVEL_3)
03996         {
03997                 WP_ForcePowerStop(self, forcePower);
03998                 return;
03999         }
04000 
04001         if (tr.fraction != 1.0f &&
04002                 tr.entityNum != gripEnt->s.number /*&&
04003                 gripLevel < FORCE_LEVEL_3*/)
04004         {
04005                 WP_ForcePowerStop(self, forcePower);
04006                 return;
04007         }
04008 
04009         if (self->client->ps.fd.forcePowerDebounce[FP_GRIP] < level.time)
04010         { //2 damage per second while choking, resulting in 10 damage total (not including The Squeeze<tm>)
04011                 self->client->ps.fd.forcePowerDebounce[FP_GRIP] = level.time + 1000;
04012                 G_Damage(gripEnt, self, self, NULL, NULL, 2, DAMAGE_NO_ARMOR, MOD_FORCE_DARK);
04013         }
04014 
04015         Jetpack_Off(gripEnt); //make sure the guy being gripped has his jetpack off.
04016 
04017         if (gripLevel == FORCE_LEVEL_1)
04018         {
04019                 gripEnt->client->ps.fd.forceGripBeingGripped = level.time + 1000;
04020                 
04021                 if ((level.time - gripEnt->client->ps.fd.forceGripStarted) > 5000)
04022                 {
04023                         WP_ForcePowerStop(self, forcePower);
04024                 }
04025                 return;
04026         }
04027 
04028         if (gripLevel == FORCE_LEVEL_2)
04029         {
04030                 gripEnt->client->ps.fd.forceGripBeingGripped = level.time + 1000;
04031 
04032                 if (gripEnt->client->ps.forceGripMoveInterval < level.time)
04033                 {
04034                         gripEnt->client->ps.velocity[2] = 30;
04035 
04036                         gripEnt->client->ps.forceGripMoveInterval = level.time + 300; //only update velocity every 300ms, so as to avoid heavy bandwidth usage
04037                 }
04038 
04039                 gripEnt->client->ps.otherKiller = self->s.number;
04040                 gripEnt->client->ps.otherKillerTime = level.time + 5000;
04041                 gripEnt->client->ps.otherKillerDebounceTime = level.time + 100;
04042 
04043                 gripEnt->client->ps.forceGripChangeMovetype = PM_FLOAT;
04044 
04045                 if ((level.time - gripEnt->client->ps.fd.forceGripStarted) > 3000 && !self->client->ps.fd.forceGripDamageDebounceTime)
04046                 { //if we managed to lift him into the air for 2 seconds, give him a crack
04047                         self->client->ps.fd.forceGripDamageDebounceTime = 1;
04048                         G_Damage(gripEnt, self, self, NULL, NULL, 20, DAMAGE_NO_ARMOR, MOD_FORCE_DARK);
04049 
04050                         //Must play custom sounds on the actual entity. Don't use G_Sound (it creates a temp entity for the sound)
04051                         G_EntitySound( gripEnt, CHAN_VOICE, G_SoundIndex(va( "*choke%d.wav", Q_irand( 1, 3 ) )) );
04052 
04053                         gripEnt->client->ps.forceHandExtend = HANDEXTEND_CHOKE;
04054                         gripEnt->client->ps.forceHandExtendTime = level.time + 2000;
04055 
04056                         if (gripEnt->client->ps.fd.forcePowersActive & (1 << FP_GRIP))
04057                         { //choking, so don't let him keep gripping himself
04058                                 WP_ForcePowerStop(gripEnt, FP_GRIP);
04059                         }
04060                 }
04061                 else if ((level.time - gripEnt->client->ps.fd.forceGripStarted) > 4000)
04062                 {
04063                         WP_ForcePowerStop(self, forcePower);
04064                 }
04065                 return;
04066         }
04067 
04068         if (gripLevel == FORCE_LEVEL_3)
04069         {
04070                 gripEnt->client->ps.fd.forceGripBeingGripped = level.time + 1000;
04071 
04072                 gripEnt->client->ps.otherKiller = self->s.number;
04073                 gripEnt->client->ps.otherKillerTime = level.time + 5000;
04074                 gripEnt->client->ps.otherKillerDebounceTime = level.time + 100;
04075 
04076                 gripEnt->client->ps.forceGripChangeMovetype = PM_FLOAT;
04077 
04078                 if (gripEnt->client->ps.forceGripMoveInterval < level.time)
04079                 {
04080                         float nvLen = 0;
04081 
04082                         VectorCopy(gripEnt->client->ps.origin, start_o);
04083                         AngleVectors(self->client->ps.viewangles, fwd, NULL, NULL);
04084                         fwd_o[0] = self->client->ps.origin[0] + fwd[0]*128;
04085                         fwd_o[1] = self->client->ps.origin[1] + fwd[1]*128;
04086                         fwd_o[2] = self->client->ps.origin[2] + fwd[2]*128;
04087                         fwd_o[2] += 16;
04088                         VectorSubtract(fwd_o, start_o, nvel);
04089 
04090                         nvLen = VectorLength(nvel);
04091 
04092                         if (nvLen < 16)
04093                         { //within x units of desired spot
04094                                 VectorNormalize(nvel);
04095                                 gripEnt->client->ps.velocity[0] = nvel[0]*8;
04096                                 gripEnt->client->ps.velocity[1] = nvel[1]*8;
04097                                 gripEnt->client->ps.velocity[2] = nvel[2]*8;
04098                         }
04099                         else if (nvLen < 64)
04100                         {
04101                                 VectorNormalize(nvel);
04102                                 gripEnt->client->ps.velocity[0] = nvel[0]*128;
04103                                 gripEnt->client->ps.velocity[1] = nvel[1]*128;
04104                                 gripEnt->client->ps.velocity[2] = nvel[2]*128;
04105                         }
04106                         else if (nvLen < 128)
04107                         {
04108                                 VectorNormalize(nvel);
04109                                 gripEnt->client->ps.velocity[0] = nvel[0]*256;
04110                                 gripEnt->client->ps.velocity[1] = nvel[1]*256;
04111                                 gripEnt->client->ps.velocity[2] = nvel[2]*256;
04112                         }
04113                         else if (nvLen < 200)
04114                         {
04115                                 VectorNormalize(nvel);
04116                                 gripEnt->client->ps.velocity[0] = nvel[0]*512;
04117                                 gripEnt->client->ps.velocity[1] = nvel[1]*512;
04118                                 gripEnt->client->ps.velocity[2] = nvel[2]*512;
04119                         }
04120                         else
04121                         {
04122                                 VectorNormalize(nvel);
04123                                 gripEnt->client->ps.velocity[0] = nvel[0]*700;
04124                                 gripEnt->client->ps.velocity[1] = nvel[1]*700;
04125                                 gripEnt->client->ps.velocity[2] = nvel[2]*700;
04126                         }
04127 
04128                         gripEnt->client->ps.forceGripMoveInterval = level.time + 300; //only update velocity every 300ms, so as to avoid heavy bandwidth usage
04129                 }
04130 
04131                 if ((level.time - gripEnt->client->ps.fd.forceGripStarted) > 3000 && !self->client->ps.fd.forceGripDamageDebounceTime)
04132                 { //if we managed to lift him into the air for 2 seconds, give him a crack
04133                         self->client->ps.fd.forceGripDamageDebounceTime = 1;
04134                         G_Damage(gripEnt, self, self, NULL, NULL, 40, DAMAGE_NO_ARMOR, MOD_FORCE_DARK);
04135 
04136                         //Must play custom sounds on the actual entity. Don't use G_Sound (it creates a temp entity for the sound)
04137                         G_EntitySound( gripEnt, CHAN_VOICE, G_SoundIndex(va( "*choke%d.wav", Q_irand( 1, 3 ) )) );
04138 
04139                         gripEnt->client->ps.forceHandExtend = HANDEXTEND_CHOKE;
04140                         gripEnt->client->ps.forceHandExtendTime = level.time + 2000;
04141 
04142                         if (gripEnt->client->ps.fd.forcePowersActive & (1 << FP_GRIP))
04143                         { //choking, so don't let him keep gripping himself
04144                                 WP_ForcePowerStop(gripEnt, FP_GRIP);
04145                         }
04146                 }
04147                 else if ((level.time - gripEnt->client->ps.fd.forceGripStarted) > 4000)
04148                 {
04149                         WP_ForcePowerStop(self, forcePower);
04150                 }
04151                 return;
04152         }
04153 }
04154 
04155 qboolean G_IsMindTricked(forcedata_t *fd, int client)
04156 {
04157         int checkIn;
04158         int trickIndex1, trickIndex2, trickIndex3, trickIndex4;
04159         int sub = 0;
04160 
04161         if (!fd)
04162         {
04163                 return qfalse;
04164         }
04165 
04166         trickIndex1 = fd->forceMindtrickTargetIndex;
04167         trickIndex2 = fd->forceMindtrickTargetIndex2;
04168         trickIndex3 = fd->forceMindtrickTargetIndex3;
04169         trickIndex4 = fd->forceMindtrickTargetIndex4;
04170 
04171         if (client > 47)
04172         {
04173                 checkIn = trickIndex4;
04174                 sub = 48;
04175         }
04176         else if (client > 31)
04177         {
04178                 checkIn = trickIndex3;
04179                 sub = 32;
04180         }
04181         else if (client > 15)
04182         {
04183                 checkIn = trickIndex2;
04184                 sub = 16;
04185         }
04186         else
04187         {
04188                 checkIn = trickIndex1;
04189         }
04190 
04191         if (checkIn & (1 << (client-sub)))
04192         {
04193                 return qtrue;
04194         }
04195         
04196         return qfalse;
04197 }
04198 
04199 static void RemoveTrickedEnt(forcedata_t *fd, int client)
04200 {
04201         if (!fd)
04202         {
04203                 return;
04204         }
04205 
04206         if (client > 47)
04207         {
04208                 fd->forceMindtrickTargetIndex4 &= ~(1 << (client-48));
04209         }
04210         else if (client > 31)
04211         {
04212                 fd->forceMindtrickTargetIndex3 &= ~(1 << (client-32));
04213         }
04214         else if (client > 15)
04215         {
04216                 fd->forceMindtrickTargetIndex2 &= ~(1 << (client-16));
04217         }
04218         else
04219         {
04220                 fd->forceMindtrickTargetIndex &= ~(1 << client);
04221         }
04222 }
04223 
04224 extern int g_LastFrameTime;
04225 extern int g_TimeSinceLastFrame;
04226 
04227 static void WP_UpdateMindtrickEnts(gentity_t *self)
04228 {
04229         int i = 0;
04230 
04231         while (i < MAX_CLIENTS)
04232         {
04233                 if (G_IsMindTricked(&self->client->ps.fd, i))
04234                 {
04235                         gentity_t *ent = &g_entities[i];
04236 
04237                         if ( !ent || !ent->client || !ent->inuse || ent->health < 1 ||
04238                                 (ent->client->ps.fd.forcePowersActive & (1 << FP_SEE)) )
04239                         {
04240                                 RemoveTrickedEnt(&self->client->ps.fd, i);
04241                         }
04242                         else if ((level.time - self->client->dangerTime) < g_TimeSinceLastFrame*4)
04243                         { //Untrick this entity if the tricker (self) fires while in his fov
04244                                 if (trap_InPVS(ent->client->ps.origin, self->client->ps.origin) &&
04245                                         OrgVisible(ent->client->ps.origin, self->client->ps.origin, ent->s.number))
04246                                 {
04247                                         RemoveTrickedEnt(&self->client->ps.fd, i);
04248                                 }
04249                         }
04250                         else if (BG_HasYsalamiri(g_gametype.integer, &ent->client->ps))
04251                         {
04252                                 RemoveTrickedEnt(&self->client->ps.fd, i);
04253                         }
04254                 }
04255 
04256                 i++;
04257         }
04258 
04259         if (!self->client->ps.fd.forceMindtrickTargetIndex &&
04260                 !self->client->ps.fd.forceMindtrickTargetIndex2 &&
04261                 !self->client->ps.fd.forceMindtrickTargetIndex3 &&
04262                 !self->client->ps.fd.forceMindtrickTargetIndex4)
04263         { //everyone who we had tricked is no longer tricked, so stop the power
04264                 WP_ForcePowerStop(self, FP_TELEPATHY);
04265         }
04266         else if (self->client->ps.powerups[PW_REDFLAG] ||
04267                 self->client->ps.powerups[PW_BLUEFLAG])
04268         {
04269                 WP_ForcePowerStop(self, FP_TELEPATHY);
04270         }
04271 }
04272 
04273 static void WP_ForcePowerRun( gentity_t *self, forcePowers_t forcePower, usercmd_t *cmd )
04274 {
04275         extern usercmd_t        ucmd;
04276 
04277         switch( (int)forcePower )
04278         {
04279         case FP_HEAL:
04280                 if (self->client->ps.fd.forcePowerLevel[FP_HEAL] == FORCE_LEVEL_1)
04281                 {
04282                         if (self->client->ps.velocity[0] || self->client->ps.velocity[1] || self->client->ps.velocity[2])
04283                         {
04284                                 WP_ForcePowerStop( self, forcePower );
04285                                 break;
04286                         }
04287                 }
04288 
04289                 if (self->health < 1 || self->client->ps.stats[STAT_HEALTH] < 1)
04290                 {
04291                         WP_ForcePowerStop( self, forcePower );
04292                         break;
04293                 }
04294 
04295                 if (self->client->ps.fd.forceHealTime > level.time)
04296                 {
04297                         break;
04298                 }
04299                 if ( self->health > self->client->ps.stats[STAT_MAX_HEALTH])
04300                 { //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
04301                         WP_ForcePowerStop( self, forcePower );
04302                         break;
04303                 }
04304                 self->client->ps.fd.forceHealTime = level.time + 1000;
04305                 self->health++;
04306                 self->client->ps.fd.forceHealAmount++;
04307 
04308                 if ( self->health > self->client->ps.stats[STAT_MAX_HEALTH])    // Past max health
04309                 {
04310                         self->health = self->client->ps.stats[STAT_MAX_HEALTH];
04311                         WP_ForcePowerStop( self, forcePower );
04312                 }
04313 
04314                 if ( (self->client->ps.fd.forcePowerLevel[FP_HEAL] == FORCE_LEVEL_1 && self->client->ps.fd.forceHealAmount >= 25) ||
04315                         (self->client->ps.fd.forcePowerLevel[FP_HEAL] == FORCE_LEVEL_2 && self->client->ps.fd.forceHealAmount >= 33))
04316                 {
04317                         WP_ForcePowerStop( self, forcePower );
04318                 }
04319                 break;
04320         case FP_SPEED:
04321                 //This is handled in PM_WalkMove and PM_StepSlideMove
04322                 if ( self->client->holdingObjectiveItem >= MAX_CLIENTS  
04323                         && self->client->holdingObjectiveItem < ENTITYNUM_WORLD )
04324                 {
04325                         if ( g_entities[self->client->holdingObjectiveItem].genericValue15 )
04326                         {//disables force powers
04327                                 WP_ForcePowerStop( self, forcePower );
04328                         }
04329                 }
04330                 /*
04331                 if ( self->client->ps.powerups[PW_REDFLAG]
04332                         || self->client->ps.powerups[PW_BLUEFLAG]
04333                         || self->client->ps.powerups[PW_NEUTRALFLAG] )
04334                 {//no force speed when carrying flag
04335                         WP_ForcePowerStop( self, forcePower );
04336                 }
04337                 */
04338                 break;
04339         case FP_GRIP:
04340                 if (self->client->ps.forceHandExtend != HANDEXTEND_FORCE_HOLD)
04341                 {
04342                         WP_ForcePowerStop(self, FP_GRIP);
04343                         break;
04344                 }
04345 
04346                 if (self->client->ps.fd.forcePowerDebounce[FP_PULL] < level.time)
04347                 { //This is sort of not ideal. Using the debounce value reserved for pull for this because pull doesn't need it.
04348                         BG_ForcePowerDrain( &self->client->ps, forcePower, 1 );
04349                         self->client->ps.fd.forcePowerDebounce[FP_PULL] = level.time + 100;
04350                 }
04351 
04352                 if (self->client->ps.fd.forcePower < 1)
04353                 {
04354                         WP_ForcePowerStop(self, FP_GRIP);
04355                         break;
04356                 }
04357 
04358                 DoGripAction(self, forcePower);
04359                 break;
04360         case FP_LEVITATION:
04361                 if ( self->client->ps.groundEntityNum != ENTITYNUM_NONE && !self->client->ps.fd.forceJumpZStart )
04362                 {//done with jump
04363                         WP_ForcePowerStop( self, forcePower );
04364                 }
04365                 break;
04366         case FP_RAGE:
04367                 if (self->health < 1)
04368                 {
04369                         WP_ForcePowerStop(self, forcePower);
04370                         break;
04371                 }
04372                 if (self->client->ps.forceRageDrainTime < level.time)
04373                 {
04374                         int addTime = 400;
04375 
04376                         self->health -= 2;
04377 
04378                         if (self->client->ps.fd.forcePowerLevel[FP_RAGE] == FORCE_LEVEL_1)
04379                         {
04380                                 addTime = 150;
04381                         }
04382                         else if (self->client->ps.fd.forcePowerLevel[FP_RAGE] == FORCE_LEVEL_2)
04383                         {
04384                                 addTime = 300;
04385                         }
04386                         else if (self->client->ps.fd.forcePowerLevel[FP_RAGE] == FORCE_LEVEL_3)
04387                         {
04388                                 addTime = 450;
04389                         }
04390                         self->client->ps.forceRageDrainTime = level.time + addTime;
04391                 }
04392 
04393                 if (self->health < 1)
04394                 {
04395                         self->health = 1;
04396                         WP_ForcePowerStop(self, forcePower);
04397                 }
04398 
04399                 self->client->ps.stats[STAT_HEALTH] = self->health;
04400                 break;
04401         case FP_DRAIN:
04402                 if (self->client->ps.forceHandExtend != HANDEXTEND_FORCE_HOLD)
04403                 {
04404                         WP_ForcePowerStop(self, forcePower);
04405                         break;
04406                 }
04407 
04408                 if ( self->client->ps.fd.forcePowerLevel[FP_DRAIN] > FORCE_LEVEL_1 )
04409                 {//higher than level 1
04410                         if ( (cmd->buttons & BUTTON_FORCE_DRAIN) || ((cmd->buttons & BUTTON_FORCEPOWER) && self->client->ps.fd.forcePowerSelected == FP_DRAIN) )
04411                         {//holding it keeps it going
04412                                 self->client->ps.fd.forcePowerDuration[FP_DRAIN] = level.time + 500;
04413                         }
04414                 }
04415                 // OVERRIDEFIXME
04416                 if ( !WP_ForcePowerAvailable( self, forcePower, 0 ) || self->client->ps.fd.forcePowerDuration[FP_DRAIN] < level.time ||
04417                         self->client->ps.fd.forcePower < 25)
04418                 {
04419                         WP_ForcePowerStop( self, forcePower );
04420                 }
04421                 else
04422                 {
04423                         ForceShootDrain( self );
04424                 }
04425                 break;
04426         case FP_LIGHTNING:
04427                 if (self->client->ps.forceHandExtend != HANDEXTEND_FORCE_HOLD)
04428                 { //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)
04429                         WP_ForcePowerStop(self, forcePower);
04430                         break;
04431                 }
04432 
04433                 if ( self->client->ps.fd.forcePowerLevel[FP_LIGHTNING] > FORCE_LEVEL_1 )
04434                 {//higher than level 1
04435                         if ( (cmd->buttons & BUTTON_FORCE_LIGHTNING) || ((cmd->buttons & BUTTON_FORCEPOWER) && self->client->ps.fd.forcePowerSelected == FP_LIGHTNING) )
04436                         {//holding it keeps it going
04437                                 self->client->ps.fd.forcePowerDuration[FP_LIGHTNING] = level.time + 500;
04438                         }
04439                 }
04440                 // OVERRIDEFIXME
04441                 if ( !WP_ForcePowerAvailable( self, forcePower, 0 ) || self->client->ps.fd.forcePowerDuration[FP_LIGHTNING] < level.time ||
04442                         self->client->ps.fd.forcePower < 25)
04443                 {
04444                         WP_ForcePowerStop( self, forcePower );
04445                 }
04446                 else
04447                 {
04448                         ForceShootLightning( self );
04449                         BG_ForcePowerDrain( &self->client->ps, forcePower, 0 );
04450                 }
04451                 break;
04452         case FP_TELEPATHY:
04453                 if ( self->client->holdingObjectiveItem >= MAX_CLIENTS  
04454                         && self->client->holdingObjectiveItem < ENTITYNUM_WORLD
04455                         && g_entities[self->client->holdingObjectiveItem].genericValue15 )
04456                 { //if force hindered can't mindtrick whilst carrying a siege item
04457                         WP_ForcePowerStop( self, FP_TELEPATHY );
04458                 }
04459                 else
04460                 {
04461                         WP_UpdateMindtrickEnts(self);
04462                 }
04463                 break;
04464         case FP_SABER_OFFENSE:
04465                 break;
04466         case FP_SABER_DEFENSE:
04467                 break;
04468         case FP_SABERTHROW:
04469                 break;
04470         case FP_PROTECT:
04471                 if (self->client->ps.fd.forcePowerDebounce[forcePower] < level.time)
04472                 {
04473                         BG_ForcePowerDrain( &self->client->ps, forcePower, 1 );
04474                         if (self->client->ps.fd.forcePower < 1)
04475                         {
04476                                 WP_ForcePowerStop(self, forcePower);
04477                         }
04478 
04479                         self->client->ps.fd.forcePowerDebounce[forcePower] = level.time + 300;
04480                 }
04481                 break;
04482         case FP_ABSORB:
04483                 if (self->client->ps.fd.forcePowerDebounce[forcePower] < level.time)
04484                 {
04485                         BG_ForcePowerDrain( &self->client->ps, forcePower, 1 );
04486                         if (self->client->ps.fd.forcePower < 1)
04487                         {
04488                                 WP_ForcePowerStop(self, forcePower);
04489                         }
04490 
04491                         self->client->ps.fd.forcePowerDebounce[forcePower] = level.time + 600;
04492                 }
04493                 break;
04494         default:
04495                 break;
04496         }
04497 }
04498 
04499 int WP_DoSpecificPower( gentity_t *self, usercmd_t *ucmd, forcePowers_t forcepower)
04500 {
04501         int powerSucceeded;
04502 
04503         powerSucceeded = 1;
04504 
04505         // OVERRIDEFIXME
04506         if ( !WP_ForcePowerAvailable( self, forcepower, 0 ) )
04507         {
04508                 return 0;
04509         }
04510 
04511         switch(forcepower)
04512         {
04513         case FP_HEAL:
04514                 powerSucceeded = 0; //always 0 for nonhold powers
04515                 if (self->client->ps.fd.forceButtonNeedRelease)
04516                 { //need to release before we can use nonhold powers again
04517                         break;
04518                 }
04519                 ForceHeal(self);
04520                 self->client->ps.fd.forceButtonNeedRelease = 1;
04521                 break;
04522         case FP_LEVITATION:
04523                 //if leave the ground by some other means, cancel the force jump so we don't suddenly jump when we land.
04524                 
04525                 if ( self->client->ps.groundEntityNum == ENTITYNUM_NONE )
04526                 {
04527                         self->client->ps.fd.forceJumpCharge = 0;
04528                         G_MuteSound( self->client->ps.fd.killSoundEntIndex[TRACK_CHANNEL_1-50], CHAN_VOICE );
04529                         //This only happens if the groundEntityNum == ENTITYNUM_NONE when the button is actually released
04530                 }
04531                 else
04532                 {//still on ground, so jump
04533                         ForceJump( self, ucmd );
04534                 }
04535                 break;
04536         case FP_SPEED:
04537                 powerSucceeded = 0; //always 0 for nonhold powers
04538                 if (self->client->ps.fd.forceButtonNeedRelease)
04539                 { //need to release before we can use nonhold powers again
04540                         break;
04541                 }
04542                 ForceSpeed(self, 0);
04543                 self->client->ps.fd.forceButtonNeedRelease = 1;
04544                 break;
04545         case FP_GRIP:
04546                 if (self->client->ps.fd.forceGripEntityNum == ENTITYNUM_NONE)
04547                 {
04548                         ForceGrip( self );
04549                 }
04550 
04551                 if (self->client->ps.fd.forceGripEntityNum != ENTITYNUM_NONE)
04552                 {
04553                         if (!(self->client->ps.fd.forcePowersActive & (1 << FP_GRIP)))
04554                         {
04555                                 WP_ForcePowerStart( self, FP_GRIP, 0 );
04556                                 BG_ForcePowerDrain( &self->client->ps, FP_GRIP, GRIP_DRAIN_AMOUNT );
04557                         }
04558                 }
04559                 else
04560                 {
04561                         powerSucceeded = 0;
04562                 }
04563                 break;
04564         case FP_LIGHTNING:
04565                 ForceLightning(self);
04566                 break;
04567         case FP_PUSH:
04568                 powerSucceeded = 0; //always 0 for nonhold powers
04569                 if (self->client->ps.fd.forceButtonNeedRelease && !(self->r.svFlags & SVF_BOT))
04570                 { //need to release before we can use nonhold powers again
04571                         break;
04572                 }
04573                 ForceThrow(self, qfalse);
04574                 self->client->ps.fd.forceButtonNeedRelease = 1;
04575                 break;
04576         case FP_PULL:
04577                 powerSucceeded = 0; //always 0 for nonhold powers
04578                 if (self->client->ps.fd.forceButtonNeedRelease)
04579                 { //need to release before we can use nonhold powers again
04580                         break;
04581                 }
04582                 ForceThrow(self, qtrue);
04583                 self->client->ps.fd.forceButtonNeedRelease = 1;
04584                 break;
04585         case FP_TELEPATHY:
04586                 powerSucceeded = 0; //always 0 for nonhold powers
04587                 if (self->client->ps.fd.forceButtonNeedRelease)
04588                 { //need to release before we can use nonhold powers again
04589                         break;
04590                 }
04591                 ForceTelepathy(self);
04592                 self->client->ps.fd.forceButtonNeedRelease = 1;
04593                 break;
04594         case FP_RAGE:
04595                 powerSucceeded = 0; //always 0 for nonhold powers
04596                 if (self->client->ps.fd.forceButtonNeedRelease)
04597                 { //need to release before we can use nonhold powers again
04598                         break;
04599                 }
04600                 ForceRage(self);
04601                 self->client->ps.fd.forceButtonNeedRelease = 1;
04602                 break;
04603         case FP_PROTECT:
04604                 powerSucceeded = 0; //always 0 for nonhold powers
04605                 if (self->client->ps.fd.forceButtonNeedRelease)
04606                 { //need to release before we can use nonhold powers again
04607                         break;
04608                 }
04609                 ForceProtect(self);
04610                 self->client->ps.fd.forceButtonNeedRelease = 1;
04611                 break;
04612         case FP_ABSORB:
04613                 powerSucceeded = 0; //always 0 for nonhold powers
04614                 if (self->client->ps.fd.forceButtonNeedRelease)
04615                 { //need to release before we can use nonhold powers again
04616                         break;
04617                 }
04618                 ForceAbsorb(self);
04619                 self->client->ps.fd.forceButtonNeedRelease = 1;
04620                 break;
04621         case FP_TEAM_HEAL:
04622                 powerSucceeded = 0; //always 0 for nonhold powers
04623                 if (self->client->ps.fd.forceButtonNeedRelease)
04624                 { //need to release before we can use nonhold powers again
04625                         break;
04626                 }
04627                 ForceTeamHeal(self);
04628                 self->client->ps.fd.forceButtonNeedRelease = 1;
04629                 break;
04630         case FP_TEAM_FORCE:
04631                 powerSucceeded = 0; //always 0 for nonhold powers
04632                 if (self->client->ps.fd.forceButtonNeedRelease)
04633                 { //need to release before we can use nonhold powers again
04634                         break;
04635                 }
04636                 ForceTeamForceReplenish(self);
04637                 self->client->ps.fd.forceButtonNeedRelease = 1;
04638                 break;
04639         case FP_DRAIN:
04640                 ForceDrain(self);
04641                 break;
04642         case FP_SEE:
04643                 powerSucceeded = 0; //always 0 for nonhold powers
04644                 if (self->client->ps.fd.forceButtonNeedRelease)
04645                 { //need to release before we can use nonhold powers again
04646                         break;
04647                 }
04648                 ForceSeeing(self);
04649                 self->client->ps.fd.forceButtonNeedRelease = 1;
04650                 break;
04651         case FP_SABER_OFFENSE:
04652                 break;
04653         case FP_SABER_DEFENSE:
04654                 break;
04655         case FP_SABERTHROW:
04656                 break;
04657         default:
04658                 break;
04659         }
04660 
04661         return powerSucceeded;
04662 }
04663 
04664 void FindGenericEnemyIndex(gentity_t *self)
04665 { //Find another client that would be considered a threat.
04666         int i = 0;
04667         float tlen;
04668         gentity_t *ent;
04669         gentity_t *besten = NULL;
04670         float blen = 99999999;
04671         vec3_t a;
04672 
04673         while (i < MAX_CLIENTS)
04674         {
04675                 ent = &g_entities[i];
04676 
04677                 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)
04678                 {
04679                         VectorSubtract(ent->client->ps.origin, self->client->ps.origin, a);
04680                         tlen = VectorLength(a);
04681 
04682                         if (tlen < blen &&
04683                                 InFront(ent->client->ps.origin, self->client->ps.origin, self->client->ps.viewangles, 0.8f ) &&
04684                                 OrgVisible(self->client->ps.origin, ent->client->ps.origin, self->s.number))
04685                         {
04686                                 blen = tlen;
04687                                 besten = ent;
04688                         }
04689                 }
04690 
04691                 i++;
04692         }
04693 
04694         if (!besten)
04695         {
04696                 return;
04697         }
04698 
04699         self->client->ps.genericEnemyIndex = besten->s.number;
04700 }
04701 
04702 void SeekerDroneUpdate(gentity_t *self)
04703 {
04704         vec3_t org, elevated, dir, a, endir;
04705         gentity_t *en;
04706         float angle;
04707         float prefig = 0;
04708         trace_t tr;
04709 
04710         if (!(self->client->ps.eFlags & EF_SEEKERDRONE))
04711         {
04712                 self->client->ps.genericEnemyIndex = -1;
04713                 return;
04714         }
04715 
04716         if (self->health < 1)
04717         {
04718                 VectorCopy(self->client->ps.origin, elevated);
04719                 elevated[2] += 40;
04720 
04721                 angle = ((level.time / 12) & 255) * (M_PI * 2) / 255; //magical numbers make magic happen
04722                 dir[0] = cos(angle) * 20;
04723                 dir[1] = sin(angle) * 20;
04724                 dir[2] = cos(angle) * 5;
04725                 VectorAdd(elevated, dir, org);
04726 
04727                 a[ROLL] = 0;
04728                 a[YAW] = 0;
04729                 a[PITCH] = 1;
04730 
04731                 G_PlayEffect(EFFECT_SPARK_EXPLOSION, org, a);
04732 
04733                 self->client->ps.eFlags -= EF_SEEKERDRONE;
04734                 self->client->ps.genericEnemyIndex = -1;
04735 
04736                 return;
04737         }
04738 
04739         if (self->client->ps.droneExistTime >= level.time && 
04740                 self->client->ps.droneExistTime < (level.time+5000))
04741         {
04742                 self->client->ps.genericEnemyIndex = 1024+self->client->ps.droneExistTime;
04743                 if (self->client->ps.droneFireTime < level.time)
04744                 {
04745                         G_Sound( self, CHAN_BODY, G_SoundIndex("sound/weapons/laser_trap/warning.wav") );
04746                         self->client->ps.droneFireTime = level.time + 100;
04747                 }
04748                 return;
04749         }
04750         else if (self->client->ps.droneExistTime < level.time)
04751         {
04752                 VectorCopy(self->client->ps.origin, elevated);
04753                 elevated[2] += 40;
04754 
04755                 prefig = (self->client->ps.droneExistTime-level.time)/80;
04756 
04757                 if (prefig > 55)
04758                 {
04759                         prefig = 55;
04760                 }
04761                 else if (prefig < 1)
04762                 {
04763                         prefig = 1;
04764                 }
04765 
04766                 elevated[2] -= 55-prefig;
04767 
04768                 angle = ((level.time / 12) & 255) * (M_PI * 2) / 255; //magical numbers make magic happen
04769                 dir[0] = cos(angle) * 20;
04770                 dir[1] = sin(angle) * 20;
04771                 dir[2] = cos(angle) * 5;
04772                 VectorAdd(elevated, dir, org);
04773 
04774                 a[ROLL] = 0;
04775                 a[YAW] = 0;
04776                 a[PITCH] = 1;
04777 
04778                 G_PlayEffect(EFFECT_SPARK_EXPLOSION, org, a);
04779 
04780                 self->client->ps.eFlags -= EF_SEEKERDRONE;
04781                 self->client->ps.genericEnemyIndex = -1;
04782 
04783                 return;
04784         }
04785 
04786         if (self->client->ps.genericEnemyIndex == -1)
04787         {
04788                 self->client->ps.genericEnemyIndex = ENTITYNUM_NONE;
04789         }
04790 
04791         if (self->client->ps.genericEnemyIndex != ENTITYNUM_NONE && self->client->ps.genericEnemyIndex != -1)
04792         {
04793                 en = &g_entities[self->client->ps.genericEnemyIndex];
04794 
04795                 if (!en || !en->client)
04796                 {
04797                         self->client->ps.genericEnemyIndex = ENTITYNUM_NONE;
04798                 }
04799                 else if (en->s.number == self->s.number)
04800                 {
04801                         self->client->ps.genericEnemyIndex = ENTITYNUM_NONE;
04802                 }
04803                 else if (en->health < 1)
04804                 {
04805                         self->client->ps.genericEnemyIndex = ENTITYNUM_NONE;
04806                 }
04807                 else if (OnSameTeam(self, en))
04808                 {
04809                         self->client->ps.genericEnemyIndex = ENTITYNUM_NONE;
04810                 }
04811                 else
04812                 {
04813                         if (!InFront(en->client->ps.origin, self->client->ps.origin, self->client->ps.viewangles, 0.8f ))
04814                         {
04815                                 self->client->ps.genericEnemyIndex = ENTITYNUM_NONE;
04816                         }
04817                         else if (!OrgVisible(self->client->ps.origin, en->client->ps.origin, self->s.number))
04818                         {
04819                                 self->client->ps.genericEnemyIndex = ENTITYNUM_NONE;
04820                         }
04821                 }
04822         }
04823 
04824         if (self->client->ps.genericEnemyIndex == ENTITYNUM_NONE || self->client->ps.genericEnemyIndex == -1)
04825         {
04826                 FindGenericEnemyIndex(self);
04827         }
04828 
04829         if (self->client->ps.genericEnemyIndex != ENTITYNUM_NONE && self->client->ps.genericEnemyIndex != -1)
04830         {
04831                 en = &g_entities[self->client->ps.genericEnemyIndex];
04832 
04833                 VectorCopy(self->client->ps.origin, elevated);
04834                 elevated[2] += 40;
04835 
04836                 angle = ((level.time / 12) & 255) * (M_PI * 2) / 255; //magical numbers make magic happen
04837                 dir[0] = cos(angle) * 20;
04838                 dir[1] = sin(angle) * 20;
04839                 dir[2] = cos(angle) * 5;
04840                 VectorAdd(elevated, dir, org);
04841 
04842                 //org is now where the thing should be client-side because it uses the same time-based offset
04843                 if (self->client->ps.droneFireTime < level.time)
04844                 {
04845                         trap_Trace(&tr, org, NULL, NULL, en->client->ps.origin, -1, MASK_SOLID);
04846 
04847                         if (tr.fraction == 1 && !tr.startsolid && !tr.allsolid)
04848                         {
04849                                 VectorSubtract(en->client->ps.origin, org, endir);
04850                                 VectorNormalize(endir);
04851 
04852                                 WP_FireGenericBlasterMissile(self, org, endir, 0, 15, 2000, MOD_BLASTER);
04853                                 G_SoundAtLoc( org, CHAN_WEAPON, G_SoundIndex("sound/weapons/bryar/fire.wav") );
04854 
04855                                 self->client->ps.droneFireTime = level.time + Q_irand(400, 700);
04856                         }
04857                 }
04858         }
04859 }
04860 
04861 void HolocronUpdate(gentity_t *self)
04862 { //keep holocron status updated in holocron mode
04863         int i = 0;
04864         int noHRank = 0;
04865 
04866         if (noHRank < FORCE_LEVEL_0)
04867         {
04868                 noHRank = FORCE_LEVEL_0;
04869         }
04870         if (noHRank > FORCE_LEVEL_3)
04871         {
04872                 noHRank = FORCE_LEVEL_3;
04873         }
04874 
04875         trap_Cvar_Update(&g_MaxHolocronCarry);
04876 
04877         while (i < NUM_FORCE_POWERS)
04878         {
04879                 if (self->client->ps.holocronsCarried[i])
04880                 { //carrying it, make sure we have the power
04881                         self->client->ps.holocronBits |= (1 << i);
04882                         self->client->ps.fd.forcePowersKnown |= (1 << i);
04883                         self->client->ps.fd.forcePowerLevel[i] = FORCE_LEVEL_3;
04884                 }
04885                 else
04886                 { //otherwise, make sure the power is cleared from us
04887                         self->client->ps.fd.forcePowerLevel[i] = 0;
04888                         if (self->client->ps.holocronBits & (1 << i))
04889                         {
04890                                 self->client->ps.holocronBits -= (1 << i);
04891                         }
04892 
04893                         if ((self->client->ps.fd.forcePowersKnown & (1 << i)) && i != FP_LEVITATION && i != FP_SABER_OFFENSE)
04894                         {
04895                                 self->client->ps.fd.forcePowersKnown -= (1 << i);
04896                         }
04897 
04898                         if ((self->client->ps.fd.forcePowersActive & (1 << i)) && i != FP_LEVITATION && i != FP_SABER_OFFENSE)
04899                         {
04900                                 WP_ForcePowerStop(self, i);
04901                         }
04902 
04903                         if (i == FP_LEVITATION)
04904                         {
04905                                 if (noHRank >= FORCE_LEVEL_1)
04906                                 {
04907                                         self->client->ps.fd.forcePowerLevel[i] = noHRank;
04908                                 }
04909                                 else
04910                                 {
04911                                         self->client->ps.fd.forcePowerLevel[i] = FORCE_LEVEL_1;
04912                                 }
04913                         }
04914                         else if (i == FP_SABER_OFFENSE)
04915                         {
04916                                 self->client->ps.fd.forcePowersKnown |= (1 << i);
04917 
04918                                 if (noHRank >= FORCE_LEVEL_1)
04919                                 {
04920                                         self->client->ps.fd.forcePowerLevel[i] = noHRank;
04921                                 }
04922                                 else
04923                                 {
04924                                         self->client->ps.fd.forcePowerLevel[i] = FORCE_LEVEL_1;
04925                                 }
04926                         }
04927                         else
04928                         {
04929                                 self->client->ps.fd.forcePowerLevel[i] = FORCE_LEVEL_0;
04930                         }
04931                 }
04932 
04933                 i++;
04934         }
04935 
04936         if (HasSetSaberOnly())
04937         { //if saberonly, we get these powers no matter what (still need the holocrons for level 3)
04938                 if (self->client->ps.fd.forcePowerLevel[FP_SABER_OFFENSE] < FORCE_LEVEL_1)
04939                 {
04940                         self->client->ps.fd.forcePowerLevel[FP_SABER_OFFENSE] = FORCE_LEVEL_1;
04941                 }
04942                 if (self->client->ps.fd.forcePowerLevel[FP_SABER_DEFENSE] < FORCE_LEVEL_1)
04943                 {
04944                         self->client->ps.fd.forcePowerLevel[FP_SABER_DEFENSE] = FORCE_LEVEL_1;
04945                 }
04946         }
04947 }
04948 
04949 void JediMasterUpdate(gentity_t *self)
04950 { //keep jedi master status updated for JM gametype
04951         int i = 0;
04952 
04953         trap_Cvar_Update(&g_MaxHolocronCarry);
04954 
04955         while (i < NUM_FORCE_POWERS)
04956         {
04957                 if (self->client->ps.isJediMaster)
04958                 {
04959                         self->client->ps.fd.forcePowersKnown |= (1 << i);
04960                         self->client->ps.fd.forcePowerLevel[i] = FORCE_LEVEL_3;
04961 
04962                         if (i == FP_TEAM_HEAL || i == FP_TEAM_FORCE ||
04963                                 i == FP_DRAIN || i == FP_ABSORB)
04964                         { //team powers are useless in JM, absorb is too because no one else has powers to absorb. Drain is just
04965                           //relatively useless in comparison, because its main intent is not to heal, but rather to cripple others
04966                           //by draining their force at the same time. And no one needs force in JM except the JM himself.
04967                                 self->client->ps.fd.forcePowersKnown &= ~(1 << i);
04968                                 self->client->ps.fd.forcePowerLevel[i] = 0;
04969                         }
04970 
04971                         if (i == FP_TELEPATHY)
04972                         { //this decision was made because level 3 mindtrick allows the JM to just hide too much, and no one else has force
04973                           //sight to counteract it. Since the JM himself is the focus of gameplay in this mode, having him hidden for large
04974                           //durations is indeed a bad thing.
04975                                 self->client->ps.fd.forcePowerLevel[i] = FORCE_LEVEL_2;
04976                         }
04977                 }
04978                 else
04979                 {
04980                         if ((self->client->ps.fd.forcePowersKnown & (1 << i)) && i != FP_LEVITATION)
04981                         {
04982                                 self->client->ps.fd.forcePowersKnown -= (1 << i);
04983                         }
04984 
04985                         if ((self->client->ps.fd.forcePowersActive & (1 << i)) && i != FP_LEVITATION)
04986                         {
04987                                 WP_ForcePowerStop(self, i);
04988                         }
04989 
04990                         if (i == FP_LEVITATION)
04991                         {
04992                                 self->client->ps.fd.forcePowerLevel[i] = FORCE_LEVEL_1;
04993                         }
04994                         else
04995                         {
04996                                 self->client->ps.fd.forcePowerLevel[i] = FORCE_LEVEL_0;
04997                         }
04998                 }
04999 
05000                 i++;
05001         }
05002 }
05003 
05004 qboolean WP_HasForcePowers( const playerState_t *ps )
05005 {
05006         int i;
05007         if ( ps )
05008         {
05009                 for ( i = 0; i < NUM_FORCE_POWERS; i++ )
05010                 {
05011                         if ( i == FP_LEVITATION )
05012                         {
05013                                 if ( ps->fd.forcePowerLevel[i] > FORCE_LEVEL_1 )
05014                                 {
05015                                         return qtrue;
05016                                 }
05017                         }
05018                         else if ( ps->fd.forcePowerLevel[i] > FORCE_LEVEL_0 )
05019                         {
05020                                 return qtrue;
05021                         }
05022                 }
05023         }
05024         return qfalse;
05025 }
05026 
05027 //try a special roll getup move
05028 qboolean G_SpecialRollGetup(gentity_t *self)
05029 { //fixme: currently no knockdown will actually land you on your front... so froll's are pretty useless at the moment.
05030         qboolean rolled = qfalse;
05031 
05032         /*
05033         if (self->client->ps.weapon != WP_SABER &&
05034                 self->client->ps.weapon != WP_MELEE)
05035         { //can't do acrobatics without saber selected
05036                 return qfalse;
05037         }
05038         */
05039 
05040         if (
05041                 self->client->pers.cmd.rightmove > 0 &&
05042                 !self->client->pers.cmd.forwardmove)
05043         {
05044                 G_SetAnim(self, &self->client->pers.cmd, SETANIM_BOTH, BOTH_GETUP_BROLL_R, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD, 0);
05045                 rolled = qtrue;
05046         }
05047         else if (
05048                 self->client->pers.cmd.rightmove < 0 &&
05049                 !self->client->pers.cmd.forwardmove)
05050         {
05051                 G_SetAnim(self, &self->client->pers.cmd, SETANIM_BOTH, BOTH_GETUP_BROLL_L, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD, 0);
05052                 rolled = qtrue;
05053         }
05054         else if (/*self->client->pers.cmd.upmove > 0 &&*/
05055                 !self->client->pers.cmd.rightmove &&
05056                 self->client->pers.cmd.forwardmove > 0)
05057         {
05058                 G_SetAnim(self, &self->client->pers.cmd, SETANIM_BOTH, BOTH_GETUP_BROLL_F, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD, 0);
05059                 rolled = qtrue;
05060         }
05061         else if (/*self->client->pers.cmd.upmove > 0 &&*/
05062                 !self->client->pers.cmd.rightmove &&
05063                 self->client->pers.cmd.forwardmove < 0)
05064         {
05065                 G_SetAnim(self, &self->client->pers.cmd, SETANIM_BOTH, BOTH_GETUP_BROLL_B, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD, 0);
05066                 rolled = qtrue;
05067         }
05068         else if (self->client->pers.cmd.upmove)
05069         {
05070                 G_PreDefSound(self->client->ps.origin, PDSOUND_FORCEJUMP);
05071                 self->client->ps.forceDodgeAnim = 2;
05072                 self->client->ps.forceHandExtendTime = level.time + 500;
05073 
05074                 //self->client->ps.velocity[2] = 300;
05075         }
05076 
05077         if (rolled)
05078         {
05079                 G_EntitySound( self, CHAN_VOICE, G_SoundIndex("*jump1.wav") );
05080         }
05081 
05082         return rolled;
05083 }
05084 
05085 void WP_ForcePowersUpdate( gentity_t *self, usercmd_t *ucmd )
05086 {
05087         qboolean        usingForce = qfalse;
05088         int                     i, holo, holoregen;
05089         int                     prepower = 0;
05090         //see if any force powers are running
05091         if ( !self )
05092         {
05093                 return;
05094         }
05095 
05096         if ( !self->client )
05097         {
05098                 return;
05099         }
05100 
05101         if (self->client->ps.pm_flags & PMF_FOLLOW)
05102         { //not a "real" game client, it's a spectator following someone
05103                 return;
05104         }
05105         if (self->client->sess.sessionTeam == TEAM_SPECTATOR)
05106         {
05107                 return;
05108         }
05109 
05110         /*
05111         if (self->client->ps.fd.saberAnimLevel > self->client->ps.fd.forcePowerLevel[FP_SABER_OFFENSE])
05112         {
05113                 self->client->ps.fd.saberAnimLevel = self->client->ps.fd.forcePowerLevel[FP_SABER_OFFENSE];
05114         }
05115         else if (!self->client->ps.fd.saberAnimLevel)
05116         {
05117                 self->client->ps.fd.saberAnimLevel = FORCE_LEVEL_1;
05118         }
05119         */
05120         //The stance in relation to power level is no longer applicable with the crazy new akimbo/staff stances.
05121         if (!self->client->ps.fd.saberAnimLevel)
05122         {
05123                 self->client->ps.fd.saberAnimLevel = FORCE_LEVEL_1;
05124         }
05125 
05126         if (g_gametype.integer != GT_SIEGE)
05127         {
05128                 if (!(self->client->ps.fd.forcePowersKnown & (1 << FP_LEVITATION)))
05129                 {
05130                         self->client->ps.fd.forcePowersKnown |= (1 << FP_LEVITATION);
05131                 }
05132 
05133                 if (self->client->ps.fd.forcePowerLevel[FP_LEVITATION] < FORCE_LEVEL_1)
05134                 {
05135                         self->client->ps.fd.forcePowerLevel[FP_LEVITATION] = FORCE_LEVEL_1;
05136                 }
05137         }
05138 
05139         if (self->client->ps.fd.forcePowerSelected < 0)
05140         { //bad
05141                 self->client->ps.fd.forcePowerSelected = 0;
05142         }
05143 
05144         if ( ((self->client->sess.selectedFP != self->client->ps.fd.forcePowerSelected) ||
05145                 (self->client->sess.saberLevel != self->client->ps.fd.saberAnimLevel)) &&
05146                 !(self->r.svFlags & SVF_BOT) )
05147         {
05148                 if (self->client->sess.updateUITime < level.time)
05149                 { //a bit hackish, but we don't want the client to flood with userinfo updates if they rapidly cycle
05150                   //through their force powers or saber attack levels
05151 
05152                         self->client->sess.selectedFP = self->client->ps.fd.forcePowerSelected;
05153                         self->client->sess.saberLevel = self->client->ps.fd.saberAnimLevel;
05154                 }
05155         }
05156 
05157         if (!g_LastFrameTime)
05158         {
05159                 g_LastFrameTime = level.time;
05160         }
05161 
05162         if (self->client->ps.forceHandExtend == HANDEXTEND_KNOCKDOWN)
05163         {
05164                 self->client->ps.zoomFov = 0;
05165                 self->client->ps.zoomMode = 0;
05166                 self->client->ps.zoomLocked = qfalse;
05167                 self->client->ps.zoomTime = 0;
05168         }
05169 
05170         if (self->client->ps.forceHandExtend == HANDEXTEND_KNOCKDOWN &&
05171                 self->client->ps.forceHandExtendTime >= level.time)
05172         {
05173                 self->client->ps.saberMove = 0;
05174                 self->client->ps.saberBlocking = 0;
05175                 self->client->ps.saberBlocked = 0;
05176                 self->client->ps.weaponTime = 0;
05177                 self->client->ps.weaponstate = WEAPON_READY;
05178         }
05179         else if (self->client->ps.forceHandExtend != HANDEXTEND_NONE &&
05180                 self->client->ps.forceHandExtendTime < level.time)
05181         {
05182                 if (self->client->ps.forceHandExtend == HANDEXTEND_KNOCKDOWN &&
05183                         !self->client->ps.forceDodgeAnim)
05184                 {
05185                         if (self->health < 1 || (self->client->ps.eFlags & EF_DEAD))
05186                         {
05187                                 self->client->ps.forceHandExtend = HANDEXTEND_NONE;
05188                         }
05189                         else if (G_SpecialRollGetup(self))
05190                         {
05191                                 self->client->ps.forceHandExtend = HANDEXTEND_NONE;
05192                         }
05193                         else
05194                         { //hmm.. ok.. no more getting up on your own, you've gotta push something, unless..
05195                                 if ((level.time-self->client->ps.forceHandExtendTime) > 4000)
05196                                 { //4 seconds elapsed, I guess they're too dumb to push something to get up!
05197                                         if (self->client->pers.cmd.upmove &&
05198                                                 self->client->ps.fd.forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_1)
05199                                         { //force getup
05200                                                 G_PreDefSound(self->client->ps.origin, PDSOUND_FORCEJUMP);
05201                                                 self->client->ps.forceDodgeAnim = 2;
05202                                                 self->client->ps.forceHandExtendTime = level.time + 500;
05203 
05204                                                 //self->client->ps.velocity[2] = 400;
05205                                         }
05206                                         else if (self->client->ps.quickerGetup)
05207                                         {
05208                                                 G_EntitySound( self, CHAN_VOICE, G_SoundIndex("*jump1.wav") );
05209                                                 self->client->ps.forceDodgeAnim = 3;
05210                                                 self->client->ps.forceHandExtendTime = level.time + 500;
05211                                                 self->client->ps.velocity[2] = 300;
05212                                         }
05213                                         else
05214                                         {
05215                                                 self->client->ps.forceDodgeAnim = 1;
05216                                                 self->client->ps.forceHandExtendTime = level.time + 1000;
05217                                         }
05218                                 }
05219                         }
05220                         self->client->ps.quickerGetup = qfalse;
05221                 }
05222                 else if (self->client->ps.forceHandExtend == HANDEXTEND_POSTTHROWN)
05223                 {
05224                         if (self->health < 1 || (self->client->ps.eFlags & EF_DEAD))
05225                         {
05226                                 self->client->ps.forceHandExtend = HANDEXTEND_NONE;
05227                         }
05228                         else if (self->client->ps.groundEntityNum != ENTITYNUM_NONE && !self->client->ps.forceDodgeAnim)
05229                         {
05230                                 self->client->ps.forceDodgeAnim = 1;
05231                                 self->client->ps.forceHandExtendTime = level.time + 1000;
05232                                 G_EntitySound( self, CHAN_VOICE, G_SoundIndex("*jump1.wav") );
05233                                 self->client->ps.velocity[2] = 100;
05234                         }
05235                         else if (!self->client->ps.forceDodgeAnim)
05236                         {
05237                                 self->client->ps.forceHandExtendTime = level.time + 100;
05238                         }
05239                         else
05240                         {
05241                                 self->client->ps.forceHandExtend = HANDEXTEND_WEAPONREADY;
05242                         }
05243                 }
05244                 else
05245                 {
05246                         self->client->ps.forceHandExtend = HANDEXTEND_WEAPONREADY;
05247                 }
05248         }
05249 
05250         if (g_gametype.integer == GT_HOLOCRON)
05251         {
05252                 HolocronUpdate(self);
05253         }
05254         if (g_gametype.integer == GT_JEDIMASTER)
05255         {
05256                 JediMasterUpdate(self);
05257         }
05258 
05259         SeekerDroneUpdate(self);
05260 
05261         if (self->client->ps.powerups[PW_FORCE_BOON])
05262         {
05263                 prepower = self->client->ps.fd.forcePower;
05264         }
05265 
05266         if (self && self->client && (BG_HasYsalamiri(g_gametype.integer, &self->client->ps) ||
05267                 self->client->ps.fd.forceDeactivateAll || self->client->tempSpectate >= level.time))
05268         { //has ysalamiri.. or we want to forcefully stop all his active powers
05269                 i = 0;
05270 
05271                 while (i < NUM_FORCE_POWERS)
05272                 {
05273                         if ((self->client->ps.fd.forcePowersActive & (1 << i)) && i != FP_LEVITATION)
05274                         {
05275                                 WP_ForcePowerStop(self, i);
05276                         }
05277 
05278                         i++;
05279                 }
05280 
05281                 if (self->client->tempSpectate >= level.time)
05282                 {
05283                         self->client->ps.fd.forcePower = 100;
05284                         self->client->ps.fd.forceRageRecoveryTime = 0;
05285                 }
05286 
05287                 self->client->ps.fd.forceDeactivateAll = 0;
05288 
05289                 if (self->client->ps.fd.forceJumpCharge)
05290                 {
05291                         G_MuteSound(self->client->ps.fd.killSoundEntIndex[TRACK_CHANNEL_1-50], CHAN_VOICE);
05292                         self->client->ps.fd.forceJumpCharge = 0;
05293                 }
05294         }
05295         else
05296         { //otherwise just do a check through them all to see if they need to be stopped for any reason.
05297                 i = 0;
05298 
05299                 while (i < NUM_FORCE_POWERS)
05300                 {
05301                         if ((self->client->ps.fd.forcePowersActive & (1 << i)) && i != FP_LEVITATION &&
05302                                 !BG_CanUseFPNow(g_gametype.integer, &self->client->ps, level.time, i))
05303                         {
05304                                 WP_ForcePowerStop(self, i);
05305                         }
05306 
05307                         i++;
05308                 }
05309         }
05310 
05311         i = 0;
05312 
05313         if (self->client->ps.powerups[PW_FORCE_ENLIGHTENED_LIGHT] || self->client->ps.powerups[PW_FORCE_ENLIGHTENED_DARK])
05314         { //enlightenment
05315                 if (!self->client->ps.fd.forceUsingAdded)
05316                 {
05317                         i = 0;
05318 
05319                         while (i < NUM_FORCE_POWERS)
05320                         {
05321                                 self->client->ps.fd.forcePowerBaseLevel[i] = self->client->ps.fd.forcePowerLevel[i];
05322 
05323                                 if (!forcePowerDarkLight[i] ||
05324                                         self->client->ps.fd.forceSide == forcePowerDarkLight[i])
05325                                 {
05326                                         self->client->ps.fd.forcePowerLevel[i] = FORCE_LEVEL_3;
05327                                         self->client->ps.fd.forcePowersKnown |= (1 << i);
05328                                 }
05329 
05330                                 i++;
05331                         }
05332 
05333                         self->client->ps.fd.forceUsingAdded = 1;
05334                 }
05335         }
05336         else if (self->client->ps.fd.forceUsingAdded)
05337         { //we don't have enlightenment but we're still using enlightened powers, so clear them back to how they should be.
05338                 i = 0;
05339 
05340                 while (i < NUM_FORCE_POWERS)
05341                 {
05342                         self->client->ps.fd.forcePowerLevel[i] = self->client->ps.fd.forcePowerBaseLevel[i];
05343                         if (!self->client->ps.fd.forcePowerLevel[i])
05344                         {
05345                                 if (self->client->ps.fd.forcePowersActive & (1 << i))
05346                                 {
05347                                         WP_ForcePowerStop(self, i);
05348                                 }
05349                                 self->client->ps.fd.forcePowersKnown &= ~(1 << i);
05350                         }
05351 
05352                         i++;
05353                 }
05354 
05355                 self->client->ps.fd.forceUsingAdded = 0;
05356         }
05357 
05358         i = 0;
05359 
05360         if (!(self->client->ps.fd.forcePowersActive & (1 << FP_TELEPATHY)))
05361         { //clear the mindtrick index values
05362                 self->client->ps.fd.forceMindtrickTargetIndex = 0;
05363                 self->client->ps.fd.forceMindtrickTargetIndex2 = 0;
05364                 self->client->ps.fd.forceMindtrickTargetIndex3 = 0;
05365                 self->client->ps.fd.forceMindtrickTargetIndex4 = 0;
05366         }
05367         
05368         if (self->health < 1)
05369         {
05370                 self->client->ps.fd.forceGripBeingGripped = 0;
05371         }
05372 
05373         if (self->client->ps.fd.forceGripBeingGripped > level.time)
05374         {
05375                 self->client->ps.fd.forceGripCripple = 1;
05376 
05377                 //keep the saber off during this period
05378                 if (self->client->ps.weapon == WP_SABER && !self->client->ps.saberHolstered)
05379                 {
05380                         Cmd_ToggleSaber_f(self);
05381                 }
05382         }
05383         else
05384         {
05385                 self->client->ps.fd.forceGripCripple = 0;
05386         }
05387 
05388         if (self->client->ps.fd.forceJumpSound)
05389         {
05390                 G_PreDefSound(self->client->ps.origin, PDSOUND_FORCEJUMP);
05391                 self->client->ps.fd.forceJumpSound = 0;
05392         }
05393 
05394         if (self->client->ps.fd.forceGripCripple)
05395         {
05396                 if (self->client->ps.fd.forceGripSoundTime < level.time)
05397                 {
05398                         G_PreDefSound(self->client->ps.origin, PDSOUND_FORCEGRIP);
05399                         self->client->ps.fd.forceGripSoundTime = level.time + 1000;
05400                 }
05401         }
05402 
05403         if (self->client->ps.fd.forcePowersActive & (1 << FP_SPEED))
05404         {
05405                 self->client->ps.powerups[PW_SPEED] = level.time + 100;
05406         }
05407 
05408         if ( self->health <= 0 )
05409         {//if dead, deactivate any active force powers
05410                 for ( i = 0; i < NUM_FORCE_POWERS; i++ )
05411                 {
05412                         if ( self->client->ps.fd.forcePowerDuration[i] || (self->client->ps.fd.forcePowersActive&( 1 << i )) )
05413                         {
05414                                 WP_ForcePowerStop( self, (forcePowers_t)i );
05415                                 self->client->ps.fd.forcePowerDuration[i] = 0;
05416                         }
05417                 }
05418                 goto powersetcheck;
05419         }
05420 
05421         if (self->client->ps.groundEntityNum != ENTITYNUM_NONE)
05422         {
05423                 self->client->fjDidJump = qfalse;
05424         }
05425 
05426         if (self->client->ps.fd.forceJumpCharge && self->client->ps.groundEntityNum == ENTITYNUM_NONE && self->client->fjDidJump)
05427         { //this was for the "charge" jump method... I guess
05428                 if (ucmd->upmove < 10 && (!(ucmd->buttons & BUTTON_FORCEPOWER) || self->client->ps.fd.forcePowerSelected != FP_LEVITATION))
05429                 {
05430                         G_MuteSound(self->client->ps.fd.killSoundEntIndex[TRACK_CHANNEL_1-50], CHAN_VOICE);
05431                         self->client->ps.fd.forceJumpCharge = 0;
05432                 }
05433         }
05434 
05435 #ifndef METROID_JUMP
05436         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(g_gametype.integer, &self->client->ps) && BG_CanUseFPNow(g_gametype.integer, &self->client->ps, level.time, FP_LEVITATION) )
05437         {//just charging up
05438                 ForceJumpCharge( self, ucmd );
05439                 usingForce = qtrue;
05440         }
05441         else if (ucmd->upmove < 10 && self->client->ps.groundEntityNum == ENTITYNUM_NONE && self->client->ps.fd.forceJumpCharge)
05442         {
05443                 self->client->ps.pm_flags &= ~(PMF_JUMP_HELD);
05444         }
05445 #endif
05446 
05447         if (!(self->client->ps.pm_flags & PMF_JUMP_HELD) && self->client->ps.fd.forceJumpCharge)
05448         {
05449                 if (!(ucmd->buttons & BUTTON_FORCEPOWER) ||
05450                         self->client->ps.fd.forcePowerSelected != FP_LEVITATION)
05451                 {
05452                         if (WP_DoSpecificPower( self, ucmd, FP_LEVITATION ))
05453                         {
05454                                 usingForce = qtrue;
05455                         }
05456                 }
05457         }
05458 
05459         if ( ucmd->buttons & BUTTON_FORCEGRIP )
05460         { //grip is one of the powers with its own button.. if it's held, call the specific grip power function.
05461                 if (WP_DoSpecificPower( self, ucmd, FP_GRIP ))
05462                 {
05463                         usingForce = qtrue;
05464                 }
05465                 else
05466                 { //don't let recharge even if the grip misses if the player still has the button down
05467                         usingForce = qtrue;
05468                 }
05469         }
05470         else
05471         { //see if we're using it generically.. if not, stop.
05472                 if (self->client->ps.fd.forcePowersActive & (1 << FP_GRIP))
05473                 {
05474                         if (!(ucmd->buttons & BUTTON_FORCEPOWER) || self->client->ps.fd.forcePowerSelected != FP_GRIP)
05475                         {
05476                                 WP_ForcePowerStop(self, FP_GRIP);
05477                         }
05478                 }
05479         }
05480 
05481         if ( ucmd->buttons & BUTTON_FORCE_LIGHTNING )
05482         { //lightning
05483                 WP_DoSpecificPower(self, ucmd, FP_LIGHTNING);
05484                 usingForce = qtrue;
05485         }
05486         else
05487         { //see if we're using it generically.. if not, stop.
05488                 if (self->client->ps.fd.forcePowersActive & (1 << FP_LIGHTNING))
05489                 {
05490                         if (!(ucmd->buttons & BUTTON_FORCEPOWER) || self->client->ps.fd.forcePowerSelected != FP_LIGHTNING)
05491                         {
05492                                 WP_ForcePowerStop(self, FP_LIGHTNING);
05493                         }
05494                 }
05495         }
05496 
05497         if ( ucmd->buttons & BUTTON_FORCE_DRAIN )
05498         { //drain
05499                 WP_DoSpecificPower(self, ucmd, FP_DRAIN);
05500                 usingForce = qtrue;
05501         }
05502         else
05503         { //see if we're using it generically.. if not, stop.
05504                 if (self->client->ps.fd.forcePowersActive & (1 << FP_DRAIN))
05505                 {
05506                         if (!(ucmd->buttons & BUTTON_FORCEPOWER) || self->client->ps.fd.forcePowerSelected != FP_DRAIN)
05507                         {
05508                                 WP_ForcePowerStop(self, FP_DRAIN);
05509                         }
05510                 }
05511         }
05512 
05513         if ( (ucmd->buttons & BUTTON_FORCEPOWER) &&
05514                 BG_CanUseFPNow(g_gametype.integer, &self->client->ps, level.time, self->client->ps.fd.forcePowerSelected))
05515         {
05516                 if (self->client->ps.fd.forcePowerSelected == FP_LEVITATION)
05517                 {
05518                         ForceJumpCharge( self, ucmd );
05519                         usingForce = qtrue;
05520                 }
05521                 else if (WP_DoSpecificPower( self, ucmd, self->client->ps.fd.forcePowerSelected ))
05522                 {
05523                         usingForce = qtrue;
05524                 }
05525                 else if (self->client->ps.fd.forcePowerSelected == FP_GRIP)
05526                 {
05527                         usingForce = qtrue;
05528                 }
05529         }
05530         else
05531         {
05532                 self->client->ps.fd.forceButtonNeedRelease = 0;
05533         }
05534 
05535         for ( i = 0; i < NUM_FORCE_POWERS; i++ )
05536         {
05537                 if ( self->client->ps.fd.forcePowerDuration[i] )
05538                 {
05539                         if ( self->client->ps.fd.forcePowerDuration[i] < level.time )
05540                         {
05541                                 if ( (self->client->ps.fd.forcePowersActive&( 1 << i )) )
05542                                 {//turn it off
05543                                         WP_ForcePowerStop( self, (forcePowers_t)i );
05544                                 }
05545                                 self->client->ps.fd.forcePowerDuration[i] = 0;
05546                         }
05547                 }
05548                 if ( (self->client->ps.fd.forcePowersActive&( 1 << i )) )
05549                 {
05550                         usingForce = qtrue;
05551                         WP_ForcePowerRun( self, (forcePowers_t)i, ucmd );
05552                 }
05553         }
05554         if ( self->client->ps.saberInFlight && self->client->ps.saberEntityNum )
05555         {//don't regen force power while throwing saber
05556                 if ( self->client->ps.saberEntityNum < ENTITYNUM_NONE && self->client->ps.saberEntityNum > 0 )//player is 0
05557                 {//
05558                         if ( &g_entities[self->client->ps.saberEntityNum] != NULL && g_entities[self->client->ps.saberEntityNum].s.pos.trType == TR_LINEAR )
05559                         {//fell to the ground and we're trying to pull it back
05560                                 usingForce = qtrue;
05561                         }
05562                 }
05563         }
05564         if ( !self->client->ps.fd.forcePowersActive || self->client->ps.fd.forcePowersActive == (1 << FP_DRAIN) )
05565         {//when not using the force, regenerate at 1 point per half second
05566                 if ( !self->client->ps.saberInFlight && self->client->ps.fd.forcePowerRegenDebounceTime < level.time &&
05567                         (self->client->ps.weapon != WP_SABER || !BG_SaberInSpecial(self->client->ps.saberMove)) )
05568                 {
05569                         if (g_gametype.integer != GT_HOLOCRON || g_MaxHolocronCarry.value)
05570                         {
05571                                 //if (!g_trueJedi.integer || self->client->ps.weapon == WP_SABER)
05572                                 //let non-jedi force regen since we're doing a more strict jedi/non-jedi thing... this gives dark jedi something to drain
05573                                 {
05574                                         if (self->client->ps.powerups[PW_FORCE_BOON])
05575                                         {
05576                                                 WP_ForcePowerRegenerate( self, 6 );
05577                                         }
05578                                         else if (self->client->ps.isJediMaster && g_gametype.integer == GT_JEDIMASTER)
05579                                         {
05580                                                 WP_ForcePowerRegenerate( self, 4 ); //jedi master regenerates 4 times as fast
05581                                         }
05582                                         else
05583                                         {
05584                                                 WP_ForcePowerRegenerate( self, 0 );
05585                                         }
05586                                 }
05587                                 /*
05588                                 else if (g_trueJedi.integer && self->client->ps.weapon != WP_SABER)
05589                                 {
05590                                         self->client->ps.fd.forcePower = 0;
05591                                 }
05592                                 */
05593                         }
05594                         else
05595                         { //regenerate based on the number of holocrons carried
05596                                 holoregen = 0;
05597                                 holo = 0;
05598                                 while (holo < NUM_FORCE_POWERS)
05599                                 {
05600                                         if (self->client->ps.holocronsCarried[holo])
05601                                         {
05602                                                 holoregen++;
05603                                         }
05604                                         holo++;
05605                                 }
05606 
05607                                 WP_ForcePowerRegenerate(self, holoregen);
05608                         }
05609 
05610                         if (g_gametype.integer == GT_SIEGE)
05611                         {
05612                                 if (self->client->holdingObjectiveItem &&
05613                                         g_entities[self->client->holdingObjectiveItem].inuse &&
05614                                         g_entities[self->client->holdingObjectiveItem].genericValue15)
05615                                 { //1 point per 7 seconds.. super slow
05616                                         self->client->ps.fd.forcePowerRegenDebounceTime = level.time + 7000;
05617                                 }
05618                                 else if (self->client->siegeClass != -1 &&
05619                                         (bgSiegeClasses[self->client->siegeClass].classflags & (1<<CFL_FASTFORCEREGEN)))
05620                                 { //if this is siege and our player class has the fast force regen ability, then recharge with 1/5th the usual delay
05621                                         self->client->ps.fd.forcePowerRegenDebounceTime = level.time + (g_forceRegenTime.integer*0.2);
05622                                 }
05623                                 else
05624                                 {
05625                                         self->client->ps.fd.forcePowerRegenDebounceTime = level.time + g_forceRegenTime.integer;
05626                                 }
05627                         }
05628                         else
05629                         {
05630                                 if ( g_gametype.integer == GT_POWERDUEL && self->client->sess.duelTeam == DUELTEAM_LONE )
05631                                 {
05632                                         if ( g_duel_fraglimit.integer )
05633                                         {
05634                                                 self->client->ps.fd.forcePowerRegenDebounceTime = level.time + (g_forceRegenTime.integer*
05635                                                         (0.6 + (.3 * (float)self->client->sess.wins / (float)g_duel_fraglimit.integer)));
05636                                         }
05637                                         else
05638                                         {
05639                                                 self->client->ps.fd.forcePowerRegenDebounceTime = level.time + (g_forceRegenTime.integer*0.7);
05640                                         }
05641                                 }
05642                                 else
05643                                 {
05644                                         self->client->ps.fd.forcePowerRegenDebounceTime = level.time + g_forceRegenTime.integer;
05645                                 }
05646                         }
05647                 }
05648         }
05649 
05650 powersetcheck:
05651 
05652         if (prepower && self->client->ps.fd.forcePower < prepower)
05653         {
05654                 int dif = ((prepower - self->client->ps.fd.forcePower)/2);
05655                 if (dif < 1)
05656                 {
05657                         dif = 1;
05658                 }
05659 
05660                 self->client->ps.fd.forcePower = (prepower-dif);
05661         }
05662 }
05663 
05664 qboolean Jedi_DodgeEvasion( gentity_t *self, gentity_t *shooter, trace_t *tr, int hitLoc )
05665 {
05666         int     dodgeAnim = -1;
05667 
05668         if ( !self || !self->client || self->health <= 0 )
05669         {
05670                 return qfalse;
05671         }
05672 
05673         if (!g_forceDodge.integer)
05674         {
05675                 return qfalse;
05676         }
05677 
05678         if (g_forceDodge.integer != 2)
05679         {
05680                 if (!(self->client->ps.fd.forcePowersActive & (1 << FP_SEE)))
05681                 {
05682                         return qfalse;
05683                 }
05684         }
05685 
05686         if ( self->client->ps.groundEntityNum == ENTITYNUM_NONE )
05687         {//can't dodge in mid-air
05688                 return qfalse;
05689         }
05690 
05691         if ( self->client->ps.weaponTime > 0 || self->client->ps.forceHandExtend != HANDEXTEND_NONE )
05692         {//in some effect that stops me from moving on my own
05693                 return qfalse;
05694         }
05695 
05696         if (g_forceDodge.integer == 2)
05697         {
05698                 if (self->client->ps.fd.forcePowersActive)
05699                 { //for now just don't let us dodge if we're using a force power at all
05700                         return qfalse;
05701                 }
05702         }
05703 
05704         if (g_forceDodge.integer == 2)
05705         {
05706                 if ( !WP_ForcePowerUsable( self, FP_SPEED ) )
05707                 {//make sure we have it and have enough force power
05708                         return qfalse;
05709                 }
05710         }
05711 
05712         if (g_forceDodge.integer == 2)
05713         {
05714                 if ( Q_irand( 1, 7 ) > self->client->ps.fd.forcePowerLevel[FP_SPEED] )
05715                 {//more likely to fail on lower force speed level
05716                         return qfalse;
05717                 }
05718         }
05719         else
05720         {
05721                 //We now dodge all the time, but only on level 3
05722                 if (self->client->ps.fd.forcePowerLevel[FP_SEE] < FORCE_LEVEL_3)
05723                 {//more likely to fail on lower force sight level
05724                         return qfalse;
05725                 }
05726         }
05727 
05728         switch( hitLoc )
05729         {
05730         case HL_NONE:
05731                 return qfalse;
05732                 break;
05733 
05734         case HL_FOOT_RT:
05735         case HL_FOOT_LT:
05736         case HL_LEG_RT:
05737         case HL_LEG_LT:
05738                 return qfalse;
05739 
05740         case HL_BACK_RT:
05741                 dodgeAnim = BOTH_DODGE_FL;
05742                 break;
05743         case HL_CHEST_RT:
05744                 dodgeAnim = BOTH_DODGE_FR;
05745                 break;
05746         case HL_BACK_LT:
05747                 dodgeAnim = BOTH_DODGE_FR;
05748                 break;
05749         case HL_CHEST_LT:
05750                 dodgeAnim = BOTH_DODGE_FR;
05751                 break;
05752         case HL_BACK:
05753         case HL_CHEST:
05754         case HL_WAIST:
05755                 dodgeAnim = BOTH_DODGE_FL;
05756                 break;
05757         case HL_ARM_RT:
05758         case HL_HAND_RT:
05759                 dodgeAnim = BOTH_DODGE_L;
05760                 break;
05761         case HL_ARM_LT:
05762         case HL_HAND_LT:
05763                 dodgeAnim = BOTH_DODGE_R;
05764                 break;
05765         case HL_HEAD:
05766                 dodgeAnim = BOTH_DODGE_FL;
05767                 break;
05768         default:
05769                 return qfalse;
05770         }
05771 
05772         if ( dodgeAnim != -1 )
05773         {
05774                 //Our own happy way of forcing an anim:
05775                 self->client->ps.forceHandExtend = HANDEXTEND_DODGE;
05776                 self->client->ps.forceDodgeAnim = dodgeAnim;
05777                 self->client->ps.forceHandExtendTime = level.time + 300;
05778 
05779                 self->client->ps.powerups[PW_SPEEDBURST] = level.time + 100;
05780 
05781                 if (g_forceDodge.integer == 2)
05782                 {
05783                         ForceSpeed( self, 500 );
05784                 }
05785                 else
05786                 {
05787                         G_Sound( self, CHAN_BODY, G_SoundIndex("sound/weapons/force/speed.wav") );
05788                 }
05789                 return qtrue;
05790         }
05791         return qfalse;
05792 }