codemp/game/g_client.c

Go to the documentation of this file.
00001 // Copyright (C) 1999-2000 Id Software, Inc.
00002 //
00003 #include "g_local.h"
00004 #include "../ghoul2/G2.h"
00005 #include "bg_saga.h"
00006 
00007 // g_client.c -- client functions that don't happen every frame
00008 
00009 static vec3_t   playerMins = {-15, -15, DEFAULT_MINS_2};
00010 static vec3_t   playerMaxs = {15, 15, DEFAULT_MAXS_2};
00011 
00012 extern int g_siegeRespawnCheck;
00013 
00014 void WP_SaberAddG2Model( gentity_t *saberent, const char *saberModel, qhandle_t saberSkin );
00015 void WP_SaberRemoveG2Model( gentity_t *saberent );
00016 extern qboolean WP_SaberStyleValidForSaber( saberInfo_t *saber1, saberInfo_t *saber2, int saberHolstered, int saberAnimLevel );
00017 extern qboolean WP_UseFirstValidSaberStyle( saberInfo_t *saber1, saberInfo_t *saber2, int saberHolstered, int *saberAnimLevel );
00018 
00019 forcedata_t Client_Force[MAX_CLIENTS];
00020 
00021 /*QUAKED info_player_duel (1 0 1) (-16 -16 -24) (16 16 32) initial
00022 potential spawning position for duelists in duel.
00023 Targets will be fired when someone spawns in on them.
00024 "nobots" will prevent bots from using this spot.
00025 "nohumans" will prevent non-bots from using this spot.
00026 */
00027 void SP_info_player_duel( gentity_t *ent )
00028 {
00029         int             i;
00030 
00031         G_SpawnInt( "nobots", "0", &i);
00032         if ( i ) {
00033                 ent->flags |= FL_NO_BOTS;
00034         }
00035         G_SpawnInt( "nohumans", "0", &i );
00036         if ( i ) {
00037                 ent->flags |= FL_NO_HUMANS;
00038         }
00039 }
00040 
00041 /*QUAKED info_player_duel1 (1 0 1) (-16 -16 -24) (16 16 32) initial
00042 potential spawning position for lone duelists in powerduel.
00043 Targets will be fired when someone spawns in on them.
00044 "nobots" will prevent bots from using this spot.
00045 "nohumans" will prevent non-bots from using this spot.
00046 */
00047 void SP_info_player_duel1( gentity_t *ent )
00048 {
00049         int             i;
00050 
00051         G_SpawnInt( "nobots", "0", &i);
00052         if ( i ) {
00053                 ent->flags |= FL_NO_BOTS;
00054         }
00055         G_SpawnInt( "nohumans", "0", &i );
00056         if ( i ) {
00057                 ent->flags |= FL_NO_HUMANS;
00058         }
00059 }
00060 
00061 /*QUAKED info_player_duel2 (1 0 1) (-16 -16 -24) (16 16 32) initial
00062 potential spawning position for paired duelists in powerduel.
00063 Targets will be fired when someone spawns in on them.
00064 "nobots" will prevent bots from using this spot.
00065 "nohumans" will prevent non-bots from using this spot.
00066 */
00067 void SP_info_player_duel2( gentity_t *ent )
00068 {
00069         int             i;
00070 
00071         G_SpawnInt( "nobots", "0", &i);
00072         if ( i ) {
00073                 ent->flags |= FL_NO_BOTS;
00074         }
00075         G_SpawnInt( "nohumans", "0", &i );
00076         if ( i ) {
00077                 ent->flags |= FL_NO_HUMANS;
00078         }
00079 }
00080 
00081 /*QUAKED info_player_deathmatch (1 0 1) (-16 -16 -24) (16 16 32) initial
00082 potential spawning position for deathmatch games.
00083 The first time a player enters the game, they will be at an 'initial' spot.
00084 Targets will be fired when someone spawns in on them.
00085 "nobots" will prevent bots from using this spot.
00086 "nohumans" will prevent non-bots from using this spot.
00087 */
00088 void SP_info_player_deathmatch( gentity_t *ent ) {
00089         int             i;
00090 
00091         G_SpawnInt( "nobots", "0", &i);
00092         if ( i ) {
00093                 ent->flags |= FL_NO_BOTS;
00094         }
00095         G_SpawnInt( "nohumans", "0", &i );
00096         if ( i ) {
00097                 ent->flags |= FL_NO_HUMANS;
00098         }
00099 }
00100 
00101 /*QUAKED info_player_start (1 0 0) (-16 -16 -24) (16 16 32)
00102 Targets will be fired when someone spawns in on them.
00103 equivelant to info_player_deathmatch
00104 */
00105 void SP_info_player_start(gentity_t *ent) {
00106         ent->classname = "info_player_deathmatch";
00107         SP_info_player_deathmatch( ent );
00108 }
00109 
00110 /*QUAKED info_player_start_red (1 0 0) (-16 -16 -24) (16 16 32) INITIAL
00111 For Red Team DM starts
00112 
00113 Targets will be fired when someone spawns in on them.
00114 equivalent to info_player_deathmatch
00115 
00116 INITIAL - The first time a player enters the game, they will be at an 'initial' spot.
00117 
00118 "nobots" will prevent bots from using this spot.
00119 "nohumans" will prevent non-bots from using this spot.
00120 */
00121 void SP_info_player_start_red(gentity_t *ent) {
00122         SP_info_player_deathmatch( ent );
00123 }
00124 
00125 /*QUAKED info_player_start_blue (1 0 0) (-16 -16 -24) (16 16 32) INITIAL
00126 For Blue Team DM starts
00127 
00128 Targets will be fired when someone spawns in on them.
00129 equivalent to info_player_deathmatch
00130 
00131 INITIAL - The first time a player enters the game, they will be at an 'initial' spot.
00132 
00133 "nobots" will prevent bots from using this spot.
00134 "nohumans" will prevent non-bots from using this spot.
00135 */
00136 void SP_info_player_start_blue(gentity_t *ent) {
00137         SP_info_player_deathmatch( ent );
00138 }
00139 
00140 void SiegePointUse( gentity_t *self, gentity_t *other, gentity_t *activator )
00141 {
00142         //Toggle the point on/off
00143         if (self->genericValue1)
00144         {
00145                 self->genericValue1 = 0;
00146         }
00147         else
00148         {
00149                 self->genericValue1 = 1;
00150         }
00151 }
00152 
00153 /*QUAKED info_player_siegeteam1 (1 0 0) (-16 -16 -24) (16 16 32)
00154 siege start point - team1
00155 name and behavior of team1 depends on what is defined in the
00156 .siege file for this level
00157 
00158 startoff - if non-0 spawn point will be disabled until used
00159 idealclass - if specified, this spawn point will be considered
00160 "ideal" for players of this class name. Corresponds to the name
00161 entry in the .scl (siege class) file.
00162 Targets will be fired when someone spawns in on them.
00163 */
00164 void SP_info_player_siegeteam1(gentity_t *ent) {
00165         int soff = 0;
00166 
00167         if (g_gametype.integer != GT_SIEGE)
00168         { //turn into a DM spawn if not in siege game mode
00169                 ent->classname = "info_player_deathmatch";
00170                 SP_info_player_deathmatch( ent );
00171 
00172                 return;
00173         }
00174 
00175         G_SpawnInt("startoff", "0", &soff);
00176 
00177         if (soff)
00178         { //start disabled
00179                 ent->genericValue1 = 0;
00180         }
00181         else
00182         {
00183                 ent->genericValue1 = 1;
00184         }
00185 
00186         ent->use = SiegePointUse;
00187 }
00188 
00189 /*QUAKED info_player_siegeteam2 (0 0 1) (-16 -16 -24) (16 16 32)
00190 siege start point - team2
00191 name and behavior of team2 depends on what is defined in the
00192 .siege file for this level
00193 
00194 startoff - if non-0 spawn point will be disabled until used
00195 idealclass - if specified, this spawn point will be considered
00196 "ideal" for players of this class name. Corresponds to the name
00197 entry in the .scl (siege class) file.
00198 Targets will be fired when someone spawns in on them.
00199 */
00200 void SP_info_player_siegeteam2(gentity_t *ent) {
00201         int soff = 0;
00202 
00203         if (g_gametype.integer != GT_SIEGE)
00204         { //turn into a DM spawn if not in siege game mode
00205                 ent->classname = "info_player_deathmatch";
00206                 SP_info_player_deathmatch( ent );
00207 
00208                 return;
00209         }
00210 
00211         G_SpawnInt("startoff", "0", &soff);
00212 
00213         if (soff)
00214         { //start disabled
00215                 ent->genericValue1 = 0;
00216         }
00217         else
00218         {
00219                 ent->genericValue1 = 1;
00220         }
00221 
00222         ent->use = SiegePointUse;
00223 }
00224 
00225 /*QUAKED info_player_intermission (1 0 1) (-16 -16 -24) (16 16 32) RED BLUE
00226 The intermission will be viewed from this point.  Target an info_notnull for the view direction.
00227 RED - In a Siege game, the intermission will happen here if the Red (attacking) team wins
00228 BLUE - In a Siege game, the intermission will happen here if the Blue (defending) team wins
00229 */
00230 void SP_info_player_intermission( gentity_t *ent ) {
00231 
00232 }
00233 
00234 /*QUAKED info_player_intermission_red (1 0 1) (-16 -16 -24) (16 16 32)
00235 The intermission will be viewed from this point.  Target an info_notnull for the view direction.
00236 
00237 In a Siege game, the intermission will happen here if the Red (attacking) team wins
00238 target - ent to look at
00239 target2 - ents to use when this intermission point is chosen
00240 */
00241 void SP_info_player_intermission_red( gentity_t *ent ) {
00242 
00243 }
00244 
00245 /*QUAKED info_player_intermission_blue (1 0 1) (-16 -16 -24) (16 16 32)
00246 The intermission will be viewed from this point.  Target an info_notnull for the view direction.
00247 
00248 In a Siege game, the intermission will happen here if the Blue (defending) team wins
00249 target - ent to look at
00250 target2 - ents to use when this intermission point is chosen
00251 */
00252 void SP_info_player_intermission_blue( gentity_t *ent ) {
00253 
00254 }
00255 
00256 #define JMSABER_RESPAWN_TIME 20000 //in case it gets stuck somewhere no one can reach
00257 
00258 void ThrowSaberToAttacker(gentity_t *self, gentity_t *attacker)
00259 {
00260         gentity_t *ent = &g_entities[self->client->ps.saberIndex];
00261         vec3_t a;
00262         int altVelocity = 0;
00263 
00264         if (!ent || ent->enemy != self)
00265         { //something has gone very wrong (this should never happen)
00266                 //but in case it does.. find the saber manually
00267 #ifdef _DEBUG
00268                 Com_Printf("Lost the saber! Attempting to use global pointer..\n");
00269 #endif
00270                 ent = gJMSaberEnt;
00271 
00272                 if (!ent)
00273                 {
00274 #ifdef _DEBUG
00275                         Com_Printf("The global pointer was NULL. This is a bad thing.\n");
00276 #endif
00277                         return;
00278                 }
00279 
00280 #ifdef _DEBUG
00281                 Com_Printf("Got it (%i). Setting enemy to client %i.\n", ent->s.number, self->s.number);
00282 #endif
00283 
00284                 ent->enemy = self;
00285                 self->client->ps.saberIndex = ent->s.number;
00286         }
00287 
00288         trap_SetConfigstring ( CS_CLIENT_JEDIMASTER, "-1" );
00289 
00290         if (attacker && attacker->client && self->client->ps.saberInFlight)
00291         { //someone killed us and we had the saber thrown, so actually move this saber to the saber location
00292           //if we killed ourselves with saber thrown, however, same suicide rules of respawning at spawn spot still
00293           //apply.
00294                 gentity_t *flyingsaber = &g_entities[self->client->ps.saberEntityNum];
00295 
00296                 if (flyingsaber && flyingsaber->inuse)
00297                 {
00298                         VectorCopy(flyingsaber->s.pos.trBase, ent->s.pos.trBase);
00299                         VectorCopy(flyingsaber->s.pos.trDelta, ent->s.pos.trDelta);
00300                         VectorCopy(flyingsaber->s.apos.trBase, ent->s.apos.trBase);
00301                         VectorCopy(flyingsaber->s.apos.trDelta, ent->s.apos.trDelta);
00302 
00303                         VectorCopy(flyingsaber->r.currentOrigin, ent->r.currentOrigin);
00304                         VectorCopy(flyingsaber->r.currentAngles, ent->r.currentAngles);
00305                         altVelocity = 1;
00306                 }
00307         }
00308 
00309         self->client->ps.saberInFlight = qtrue; //say he threw it anyway in order to properly remove from dead body
00310 
00311         WP_SaberAddG2Model( ent, self->client->saber[0].model, self->client->saber[0].skin );
00312 
00313         ent->s.eFlags &= ~(EF_NODRAW);
00314         ent->s.modelGhoul2 = 1;
00315         ent->s.eType = ET_MISSILE;
00316         ent->enemy = NULL;
00317 
00318         if (!attacker || !attacker->client)
00319         {
00320                 VectorCopy(ent->s.origin2, ent->s.pos.trBase);
00321                 VectorCopy(ent->s.origin2, ent->s.origin);
00322                 VectorCopy(ent->s.origin2, ent->r.currentOrigin);
00323                 ent->pos2[0] = 0;
00324                 trap_LinkEntity(ent);
00325                 return;
00326         }
00327 
00328         if (!altVelocity)
00329         {
00330                 VectorCopy(self->s.pos.trBase, ent->s.pos.trBase);
00331                 VectorCopy(self->s.pos.trBase, ent->s.origin);
00332                 VectorCopy(self->s.pos.trBase, ent->r.currentOrigin);
00333 
00334                 VectorSubtract(attacker->client->ps.origin, ent->s.pos.trBase, a);
00335 
00336                 VectorNormalize(a);
00337 
00338                 ent->s.pos.trDelta[0] = a[0]*256;
00339                 ent->s.pos.trDelta[1] = a[1]*256;
00340                 ent->s.pos.trDelta[2] = 256;
00341         }
00342 
00343         trap_LinkEntity(ent);
00344 }
00345 
00346 void JMSaberThink(gentity_t *ent)
00347 {
00348         gJMSaberEnt = ent;
00349 
00350         if (ent->enemy)
00351         {
00352                 if (!ent->enemy->client || !ent->enemy->inuse)
00353                 { //disconnected?
00354                         VectorCopy(ent->enemy->s.pos.trBase, ent->s.pos.trBase);
00355                         VectorCopy(ent->enemy->s.pos.trBase, ent->s.origin);
00356                         VectorCopy(ent->enemy->s.pos.trBase, ent->r.currentOrigin);
00357                         ent->s.modelindex = G_ModelIndex("models/weapons2/saber/saber_w.glm");
00358                         ent->s.eFlags &= ~(EF_NODRAW);
00359                         ent->s.modelGhoul2 = 1;
00360                         ent->s.eType = ET_MISSILE;
00361                         ent->enemy = NULL;
00362 
00363                         ent->pos2[0] = 1;
00364                         ent->pos2[1] = 0; //respawn next think
00365                         trap_LinkEntity(ent);
00366                 }
00367                 else
00368                 {
00369                         ent->pos2[1] = level.time + JMSABER_RESPAWN_TIME;
00370                 }
00371         }
00372         else if (ent->pos2[0] && ent->pos2[1] < level.time)
00373         {
00374                 VectorCopy(ent->s.origin2, ent->s.pos.trBase);
00375                 VectorCopy(ent->s.origin2, ent->s.origin);
00376                 VectorCopy(ent->s.origin2, ent->r.currentOrigin);
00377                 ent->pos2[0] = 0;
00378                 trap_LinkEntity(ent);
00379         }
00380 
00381         ent->nextthink = level.time + 50;
00382         G_RunObject(ent);
00383 }
00384 
00385 void JMSaberTouch(gentity_t *self, gentity_t *other, trace_t *trace)
00386 {
00387         int i = 0;
00388 //      gentity_t *te;
00389 
00390         if (!other || !other->client || other->health < 1)
00391         {
00392                 return;
00393         }
00394 
00395         if (self->enemy)
00396         {
00397                 return;
00398         }
00399 
00400         if (!self->s.modelindex)
00401         {
00402                 return;
00403         }
00404 
00405         if (other->client->ps.stats[STAT_WEAPONS] & (1 << WP_SABER))
00406         {
00407                 return;
00408         }
00409 
00410         if (other->client->ps.isJediMaster)
00411         {
00412                 return;
00413         }
00414 
00415         self->enemy = other;
00416         other->client->ps.stats[STAT_WEAPONS] = (1 << WP_SABER);
00417         other->client->ps.weapon = WP_SABER;
00418         other->s.weapon = WP_SABER;
00419         G_AddEvent(other, EV_BECOME_JEDIMASTER, 0);
00420 
00421         // Track the jedi master 
00422         trap_SetConfigstring ( CS_CLIENT_JEDIMASTER, va("%i", other->s.number ) );
00423 
00424         if (g_spawnInvulnerability.integer)
00425         {
00426                 other->client->ps.eFlags |= EF_INVULNERABLE;
00427                 other->client->invulnerableTimer = level.time + g_spawnInvulnerability.integer;
00428         }
00429 
00430         trap_SendServerCommand( -1, va("cp \"%s %s\n\"", other->client->pers.netname, G_GetStringEdString("MP_SVGAME", "BECOMEJM")) );
00431 
00432         other->client->ps.isJediMaster = qtrue;
00433         other->client->ps.saberIndex = self->s.number;
00434 
00435         if (other->health < 200 && other->health > 0)
00436         { //full health when you become the Jedi Master
00437                 other->client->ps.stats[STAT_HEALTH] = other->health = 200;
00438         }
00439 
00440         if (other->client->ps.fd.forcePower < 100)
00441         {
00442                 other->client->ps.fd.forcePower = 100;
00443         }
00444 
00445         while (i < NUM_FORCE_POWERS)
00446         {
00447                 other->client->ps.fd.forcePowersKnown |= (1 << i);
00448                 other->client->ps.fd.forcePowerLevel[i] = FORCE_LEVEL_3;
00449 
00450                 i++;
00451         }
00452 
00453         self->pos2[0] = 1;
00454         self->pos2[1] = level.time + JMSABER_RESPAWN_TIME;
00455 
00456         self->s.modelindex = 0;
00457         self->s.eFlags |= EF_NODRAW;
00458         self->s.modelGhoul2 = 0;
00459         self->s.eType = ET_GENERAL;
00460 
00461         /*
00462         te = G_TempEntity( vec3_origin, EV_DESTROY_GHOUL2_INSTANCE );
00463         te->r.svFlags |= SVF_BROADCAST;
00464         te->s.eventParm = self->s.number;
00465         */
00466         G_KillG2Queue(self->s.number);
00467 
00468         return;
00469 }
00470 
00471 gentity_t *gJMSaberEnt = NULL;
00472 
00473 /*QUAKED info_jedimaster_start (1 0 0) (-16 -16 -24) (16 16 32)
00474 "jedi master" saber spawn point
00475 */
00476 void SP_info_jedimaster_start(gentity_t *ent)
00477 {
00478         if (g_gametype.integer != GT_JEDIMASTER)
00479         {
00480                 gJMSaberEnt = NULL;
00481                 G_FreeEntity(ent);
00482                 return;
00483         }
00484 
00485         ent->enemy = NULL;
00486 
00487         ent->flags = FL_BOUNCE_HALF;
00488 
00489         ent->s.modelindex = G_ModelIndex("models/weapons2/saber/saber_w.glm");
00490         ent->s.modelGhoul2 = 1;
00491         ent->s.g2radius = 20;
00492         //ent->s.eType = ET_GENERAL;
00493         ent->s.eType = ET_MISSILE;
00494         ent->s.weapon = WP_SABER;
00495         ent->s.pos.trType = TR_GRAVITY;
00496         ent->s.pos.trTime = level.time;
00497         VectorSet( ent->r.maxs, 3, 3, 3 );
00498         VectorSet( ent->r.mins, -3, -3, -3 );
00499         ent->r.contents = CONTENTS_TRIGGER;
00500         ent->clipmask = MASK_SOLID;
00501 
00502         ent->isSaberEntity = qtrue;
00503 
00504         ent->bounceCount = -5;
00505 
00506         ent->physicsObject = qtrue;
00507 
00508         VectorCopy(ent->s.pos.trBase, ent->s.origin2); //remember the spawn spot
00509 
00510         ent->touch = JMSaberTouch;
00511 
00512         trap_LinkEntity(ent);
00513 
00514         ent->think = JMSaberThink;
00515         ent->nextthink = level.time + 50;
00516 }
00517 
00518 /*
00519 =======================================================================
00520 
00521   SelectSpawnPoint
00522 
00523 =======================================================================
00524 */
00525 
00526 /*
00527 ================
00528 SpotWouldTelefrag
00529 
00530 ================
00531 */
00532 qboolean SpotWouldTelefrag( gentity_t *spot ) {
00533         int                     i, num;
00534         int                     touch[MAX_GENTITIES];
00535         gentity_t       *hit;
00536         vec3_t          mins, maxs;
00537 
00538         VectorAdd( spot->s.origin, playerMins, mins );
00539         VectorAdd( spot->s.origin, playerMaxs, maxs );
00540         num = trap_EntitiesInBox( mins, maxs, touch, MAX_GENTITIES );
00541 
00542         for (i=0 ; i<num ; i++) {
00543                 hit = &g_entities[touch[i]];
00544                 //if ( hit->client && hit->client->ps.stats[STAT_HEALTH] > 0 ) {
00545                 if ( hit->client) {
00546                         return qtrue;
00547                 }
00548 
00549         }
00550 
00551         return qfalse;
00552 }
00553 
00554 qboolean SpotWouldTelefrag2( gentity_t *mover, vec3_t dest ) 
00555 {
00556         int                     i, num;
00557         int                     touch[MAX_GENTITIES];
00558         gentity_t       *hit;
00559         vec3_t          mins, maxs;
00560 
00561         VectorAdd( dest, mover->r.mins, mins );
00562         VectorAdd( dest, mover->r.maxs, maxs );
00563         num = trap_EntitiesInBox( mins, maxs, touch, MAX_GENTITIES );
00564 
00565         for (i=0 ; i<num ; i++) 
00566         {
00567                 hit = &g_entities[touch[i]];
00568                 if ( hit == mover )
00569                 {
00570                         continue;
00571                 }
00572 
00573                 if ( hit->r.contents & mover->r.contents )
00574                 {
00575                         return qtrue;
00576                 }
00577         }
00578 
00579         return qfalse;
00580 }
00581 
00582 /*
00583 ================
00584 SelectNearestDeathmatchSpawnPoint
00585 
00586 Find the spot that we DON'T want to use
00587 ================
00588 */
00589 #define MAX_SPAWN_POINTS        128
00590 gentity_t *SelectNearestDeathmatchSpawnPoint( vec3_t from ) {
00591         gentity_t       *spot;
00592         vec3_t          delta;
00593         float           dist, nearestDist;
00594         gentity_t       *nearestSpot;
00595 
00596         nearestDist = 999999;
00597         nearestSpot = NULL;
00598         spot = NULL;
00599 
00600         while ((spot = G_Find (spot, FOFS(classname), "info_player_deathmatch")) != NULL) {
00601 
00602                 VectorSubtract( spot->s.origin, from, delta );
00603                 dist = VectorLength( delta );
00604                 if ( dist < nearestDist ) {
00605                         nearestDist = dist;
00606                         nearestSpot = spot;
00607                 }
00608         }
00609 
00610         return nearestSpot;
00611 }
00612 
00613 
00614 /*
00615 ================
00616 SelectRandomDeathmatchSpawnPoint
00617 
00618 go to a random point that doesn't telefrag
00619 ================
00620 */
00621 #define MAX_SPAWN_POINTS        128
00622 gentity_t *SelectRandomDeathmatchSpawnPoint( void ) {
00623         gentity_t       *spot;
00624         int                     count;
00625         int                     selection;
00626         gentity_t       *spots[MAX_SPAWN_POINTS];
00627 
00628         count = 0;
00629         spot = NULL;
00630 
00631         while ((spot = G_Find (spot, FOFS(classname), "info_player_deathmatch")) != NULL) {
00632                 if ( SpotWouldTelefrag( spot ) ) {
00633                         continue;
00634                 }
00635                 spots[ count ] = spot;
00636                 count++;
00637         }
00638 
00639         if ( !count ) { // no spots that won't telefrag
00640                 return G_Find( NULL, FOFS(classname), "info_player_deathmatch");
00641         }
00642 
00643         selection = rand() % count;
00644         return spots[ selection ];
00645 }
00646 
00647 /*
00648 ===========
00649 SelectRandomFurthestSpawnPoint
00650 
00651 Chooses a player start, deathmatch start, etc
00652 ============
00653 */
00654 gentity_t *SelectRandomFurthestSpawnPoint ( vec3_t avoidPoint, vec3_t origin, vec3_t angles, team_t team ) {
00655         gentity_t       *spot;
00656         vec3_t          delta;
00657         float           dist;
00658         float           list_dist[64];
00659         gentity_t       *list_spot[64];
00660         int                     numSpots, rnd, i, j;
00661 
00662         numSpots = 0;
00663         spot = NULL;
00664 
00665         //in Team DM, look for a team start spot first, if any
00666         if ( g_gametype.integer == GT_TEAM 
00667                 && team != TEAM_FREE 
00668                 && team != TEAM_SPECTATOR )
00669         {
00670                 char *classname = NULL;
00671                 if ( team == TEAM_RED )
00672                 {
00673                         classname = "info_player_start_red";
00674                 }
00675                 else
00676                 {
00677                         classname = "info_player_start_blue";
00678                 }
00679                 while ((spot = G_Find (spot, FOFS(classname), classname)) != NULL) {
00680                         if ( SpotWouldTelefrag( spot ) ) {
00681                                 continue;
00682                         }
00683                         VectorSubtract( spot->s.origin, avoidPoint, delta );
00684                         dist = VectorLength( delta );
00685                         for (i = 0; i < numSpots; i++) {
00686                                 if ( dist > list_dist[i] ) {
00687                                         if ( numSpots >= 64 )
00688                                                 numSpots = 64-1;
00689                                         for (j = numSpots; j > i; j--) {
00690                                                 list_dist[j] = list_dist[j-1];
00691                                                 list_spot[j] = list_spot[j-1];
00692                                         }
00693                                         list_dist[i] = dist;
00694                                         list_spot[i] = spot;
00695                                         numSpots++;
00696                                         if (numSpots > 64)
00697                                                 numSpots = 64;
00698                                         break;
00699                                 }
00700                         }
00701                         if (i >= numSpots && numSpots < 64) {
00702                                 list_dist[numSpots] = dist;
00703                                 list_spot[numSpots] = spot;
00704                                 numSpots++;
00705                         }
00706                 }
00707         }
00708 
00709         if ( !numSpots )
00710         {//couldn't find any of the above
00711                 while ((spot = G_Find (spot, FOFS(classname), "info_player_deathmatch")) != NULL) {
00712                         if ( SpotWouldTelefrag( spot ) ) {
00713                                 continue;
00714                         }
00715                         VectorSubtract( spot->s.origin, avoidPoint, delta );
00716                         dist = VectorLength( delta );
00717                         for (i = 0; i < numSpots; i++) {
00718                                 if ( dist > list_dist[i] ) {
00719                                         if ( numSpots >= 64 )
00720                                                 numSpots = 64-1;
00721                                         for (j = numSpots; j > i; j--) {
00722                                                 list_dist[j] = list_dist[j-1];
00723                                                 list_spot[j] = list_spot[j-1];
00724                                         }
00725                                         list_dist[i] = dist;
00726                                         list_spot[i] = spot;
00727                                         numSpots++;
00728                                         if (numSpots > 64)
00729                                                 numSpots = 64;
00730                                         break;
00731                                 }
00732                         }
00733                         if (i >= numSpots && numSpots < 64) {
00734                                 list_dist[numSpots] = dist;
00735                                 list_spot[numSpots] = spot;
00736                                 numSpots++;
00737                         }
00738                 }
00739                 if (!numSpots) {
00740                         spot = G_Find( NULL, FOFS(classname), "info_player_deathmatch");
00741                         if (!spot)
00742                                 G_Error( "Couldn't find a spawn point" );
00743                         VectorCopy (spot->s.origin, origin);
00744                         origin[2] += 9;
00745                         VectorCopy (spot->s.angles, angles);
00746                         return spot;
00747                 }
00748         }
00749 
00750         // select a random spot from the spawn points furthest away
00751         rnd = random() * (numSpots / 2);
00752 
00753         VectorCopy (list_spot[rnd]->s.origin, origin);
00754         origin[2] += 9;
00755         VectorCopy (list_spot[rnd]->s.angles, angles);
00756 
00757         return list_spot[rnd];
00758 }
00759 
00760 gentity_t *SelectDuelSpawnPoint( int team, vec3_t avoidPoint, vec3_t origin, vec3_t angles )
00761 {
00762         gentity_t       *spot;
00763         vec3_t          delta;
00764         float           dist;
00765         float           list_dist[64];
00766         gentity_t       *list_spot[64];
00767         int                     numSpots, rnd, i, j;
00768         char            *spotName;
00769 
00770         if (team == DUELTEAM_LONE)
00771         {
00772                 spotName = "info_player_duel1";
00773         }
00774         else if (team == DUELTEAM_DOUBLE)
00775         {
00776                 spotName = "info_player_duel2";
00777         }
00778         else if (team == DUELTEAM_SINGLE)
00779         {
00780                 spotName = "info_player_duel";
00781         }
00782         else
00783         {
00784                 spotName = "info_player_deathmatch";
00785         }
00786 tryAgain:
00787 
00788         numSpots = 0;
00789         spot = NULL;
00790 
00791         while ((spot = G_Find (spot, FOFS(classname), spotName)) != NULL) {
00792                 if ( SpotWouldTelefrag( spot ) ) {
00793                         continue;
00794                 }
00795                 VectorSubtract( spot->s.origin, avoidPoint, delta );
00796                 dist = VectorLength( delta );
00797                 for (i = 0; i < numSpots; i++) {
00798                         if ( dist > list_dist[i] ) {
00799                                 if ( numSpots >= 64 )
00800                                         numSpots = 64-1;
00801                                 for (j = numSpots; j > i; j--) {
00802                                         list_dist[j] = list_dist[j-1];
00803                                         list_spot[j] = list_spot[j-1];
00804                                 }
00805                                 list_dist[i] = dist;
00806                                 list_spot[i] = spot;
00807                                 numSpots++;
00808                                 if (numSpots > 64)
00809                                         numSpots = 64;
00810                                 break;
00811                         }
00812                 }
00813                 if (i >= numSpots && numSpots < 64) {
00814                         list_dist[numSpots] = dist;
00815                         list_spot[numSpots] = spot;
00816                         numSpots++;
00817                 }
00818         }
00819         if (!numSpots)
00820         {
00821                 if (Q_stricmp(spotName, "info_player_deathmatch"))
00822                 { //try the loop again with info_player_deathmatch as the target if we couldn't find a duel spot
00823                         spotName = "info_player_deathmatch";
00824                         goto tryAgain;
00825                 }
00826 
00827                 //If we got here we found no free duel or DM spots, just try the first DM spot
00828                 spot = G_Find( NULL, FOFS(classname), "info_player_deathmatch");
00829                 if (!spot)
00830                         G_Error( "Couldn't find a spawn point" );
00831                 VectorCopy (spot->s.origin, origin);
00832                 origin[2] += 9;
00833                 VectorCopy (spot->s.angles, angles);
00834                 return spot;
00835         }
00836 
00837         // select a random spot from the spawn points furthest away
00838         rnd = random() * (numSpots / 2);
00839 
00840         VectorCopy (list_spot[rnd]->s.origin, origin);
00841         origin[2] += 9;
00842         VectorCopy (list_spot[rnd]->s.angles, angles);
00843 
00844         return list_spot[rnd];
00845 }
00846 
00847 /*
00848 ===========
00849 SelectSpawnPoint
00850 
00851 Chooses a player start, deathmatch start, etc
00852 ============
00853 */
00854 gentity_t *SelectSpawnPoint ( vec3_t avoidPoint, vec3_t origin, vec3_t angles, team_t team ) {
00855         return SelectRandomFurthestSpawnPoint( avoidPoint, origin, angles, team );
00856 
00857         /*
00858         gentity_t       *spot;
00859         gentity_t       *nearestSpot;
00860 
00861         nearestSpot = SelectNearestDeathmatchSpawnPoint( avoidPoint );
00862 
00863         spot = SelectRandomDeathmatchSpawnPoint ( );
00864         if ( spot == nearestSpot ) {
00865                 // roll again if it would be real close to point of death
00866                 spot = SelectRandomDeathmatchSpawnPoint ( );
00867                 if ( spot == nearestSpot ) {
00868                         // last try
00869                         spot = SelectRandomDeathmatchSpawnPoint ( );
00870                 }               
00871         }
00872 
00873         // find a single player start spot
00874         if (!spot) {
00875                 G_Error( "Couldn't find a spawn point" );
00876         }
00877 
00878         VectorCopy (spot->s.origin, origin);
00879         origin[2] += 9;
00880         VectorCopy (spot->s.angles, angles);
00881 
00882         return spot;
00883         */
00884 }
00885 
00886 /*
00887 ===========
00888 SelectInitialSpawnPoint
00889 
00890 Try to find a spawn point marked 'initial', otherwise
00891 use normal spawn selection.
00892 ============
00893 */
00894 gentity_t *SelectInitialSpawnPoint( vec3_t origin, vec3_t angles, team_t team ) {
00895         gentity_t       *spot;
00896 
00897         spot = NULL;
00898         while ((spot = G_Find (spot, FOFS(classname), "info_player_deathmatch")) != NULL) {
00899                 if ( spot->spawnflags & 1 ) {
00900                         break;
00901                 }
00902         }
00903 
00904         if ( !spot || SpotWouldTelefrag( spot ) ) {
00905                 return SelectSpawnPoint( vec3_origin, origin, angles, team );
00906         }
00907 
00908         VectorCopy (spot->s.origin, origin);
00909         origin[2] += 9;
00910         VectorCopy (spot->s.angles, angles);
00911 
00912         return spot;
00913 }
00914 
00915 /*
00916 ===========
00917 SelectSpectatorSpawnPoint
00918 
00919 ============
00920 */
00921 gentity_t *SelectSpectatorSpawnPoint( vec3_t origin, vec3_t angles ) {
00922         FindIntermissionPoint();
00923 
00924         VectorCopy( level.intermission_origin, origin );
00925         VectorCopy( level.intermission_angle, angles );
00926 
00927         return NULL;
00928 }
00929 
00930 /*
00931 =======================================================================
00932 
00933 BODYQUE
00934 
00935 =======================================================================
00936 */
00937 
00938 /*
00939 =======================================================================
00940 
00941 BODYQUE
00942 
00943 =======================================================================
00944 */
00945 
00946 #define BODY_SINK_TIME          30000//45000
00947 
00948 /*
00949 ===============
00950 InitBodyQue
00951 ===============
00952 */
00953 void InitBodyQue (void) {
00954         int             i;
00955         gentity_t       *ent;
00956 
00957         level.bodyQueIndex = 0;
00958         for (i=0; i<BODY_QUEUE_SIZE ; i++) {
00959                 ent = G_Spawn();
00960                 ent->classname = "bodyque";
00961                 ent->neverFree = qtrue;
00962                 level.bodyQue[i] = ent;
00963         }
00964 }
00965 
00966 /*
00967 =============
00968 BodySink
00969 
00970 After sitting around for five seconds, fall into the ground and dissapear
00971 =============
00972 */
00973 void BodySink( gentity_t *ent ) {
00974         if ( level.time - ent->timestamp > BODY_SINK_TIME + 2500 ) {
00975                 // the body ques are never actually freed, they are just unlinked
00976                 trap_UnlinkEntity( ent );
00977                 ent->physicsObject = qfalse;
00978                 return; 
00979         }
00980 //      ent->nextthink = level.time + 100;
00981 //      ent->s.pos.trBase[2] -= 1;
00982 
00983         G_AddEvent(ent, EV_BODYFADE, 0);
00984         ent->nextthink = level.time + 18000;
00985         ent->takedamage = qfalse;
00986 }
00987 
00988 /*
00989 =============
00990 CopyToBodyQue
00991 
00992 A player is respawning, so make an entity that looks
00993 just like the existing corpse to leave behind.
00994 =============
00995 */
00996 static qboolean CopyToBodyQue( gentity_t *ent ) {
00997         gentity_t               *body;
00998         int                     contents;
00999         int                     islight = 0;
01000 
01001         if (level.intermissiontime)
01002         {
01003                 return qfalse;
01004         }
01005 
01006         trap_UnlinkEntity (ent);
01007 
01008         // if client is in a nodrop area, don't leave the body
01009         contents = trap_PointContents( ent->s.origin, -1 );
01010         if ( contents & CONTENTS_NODROP ) {
01011                 return qfalse;
01012         }
01013 
01014         if (ent->client && (ent->client->ps.eFlags & EF_DISINTEGRATION))
01015         { //for now, just don't spawn a body if you got disint'd
01016                 return qfalse;
01017         }
01018 
01019         // grab a body que and cycle to the next one
01020         body = level.bodyQue[ level.bodyQueIndex ];
01021         level.bodyQueIndex = (level.bodyQueIndex + 1) % BODY_QUEUE_SIZE;
01022 
01023         trap_UnlinkEntity (body);
01024         body->s = ent->s;
01025 
01026         //avoid oddly angled corpses floating around
01027         body->s.angles[PITCH] = body->s.angles[ROLL] = body->s.apos.trBase[PITCH] = body->s.apos.trBase[ROLL] = 0;
01028 
01029         body->s.g2radius = 100;
01030 
01031         body->s.eType = ET_BODY;
01032         body->s.eFlags = EF_DEAD;               // clear EF_TALK, etc
01033 
01034         if (ent->client && (ent->client->ps.eFlags & EF_DISINTEGRATION))
01035         {
01036                 body->s.eFlags |= EF_DISINTEGRATION;
01037         }
01038 
01039         VectorCopy(ent->client->ps.lastHitLoc, body->s.origin2);
01040 
01041         body->s.powerups = 0;   // clear powerups
01042         body->s.loopSound = 0;  // clear lava burning
01043         body->s.loopIsSoundset = qfalse;
01044         body->s.number = body - g_entities;
01045         body->timestamp = level.time;
01046         body->physicsObject = qtrue;
01047         body->physicsBounce = 0;                // don't bounce
01048         if ( body->s.groundEntityNum == ENTITYNUM_NONE ) {
01049                 body->s.pos.trType = TR_GRAVITY;
01050                 body->s.pos.trTime = level.time;
01051                 VectorCopy( ent->client->ps.velocity, body->s.pos.trDelta );
01052         } else {
01053                 body->s.pos.trType = TR_STATIONARY;
01054         }
01055         body->s.event = 0;
01056 
01057         body->s.weapon = ent->s.bolt2;
01058 
01059         if (body->s.weapon == WP_SABER && ent->client->ps.saberInFlight)
01060         {
01061                 body->s.weapon = WP_BLASTER; //lie to keep from putting a saber on the corpse, because it was thrown at death
01062         }
01063 
01064         //G_AddEvent(body, EV_BODY_QUEUE_COPY, ent->s.clientNum);
01065         //Now doing this through a modified version of the rcg reliable command.
01066         if (ent->client && ent->client->ps.fd.forceSide == FORCE_LIGHTSIDE)
01067         {
01068                 islight = 1;
01069         }
01070         trap_SendServerCommand(-1, va("ircg %i %i %i %i", ent->s.number, body->s.number, body->s.weapon, islight));
01071 
01072         body->r.svFlags = ent->r.svFlags | SVF_BROADCAST;
01073         VectorCopy (ent->r.mins, body->r.mins);
01074         VectorCopy (ent->r.maxs, body->r.maxs);
01075         VectorCopy (ent->r.absmin, body->r.absmin);
01076         VectorCopy (ent->r.absmax, body->r.absmax);
01077 
01078         body->s.torsoAnim = body->s.legsAnim = ent->client->ps.legsAnim;
01079 
01080         body->s.customRGBA[0] = ent->client->ps.customRGBA[0];
01081         body->s.customRGBA[1] = ent->client->ps.customRGBA[1];
01082         body->s.customRGBA[2] = ent->client->ps.customRGBA[2];
01083         body->s.customRGBA[3] = ent->client->ps.customRGBA[3];
01084 
01085         body->clipmask = CONTENTS_SOLID | CONTENTS_PLAYERCLIP;
01086         body->r.contents = CONTENTS_CORPSE;
01087         body->r.ownerNum = ent->s.number;
01088 
01089         body->nextthink = level.time + BODY_SINK_TIME;
01090         body->think = BodySink;
01091 
01092         body->die = body_die;
01093 
01094         // don't take more damage if already gibbed
01095         if ( ent->health <= GIB_HEALTH ) {
01096                 body->takedamage = qfalse;
01097         } else {
01098                 body->takedamage = qtrue;
01099         }
01100 
01101         VectorCopy ( body->s.pos.trBase, body->r.currentOrigin );
01102         trap_LinkEntity (body);
01103 
01104         return qtrue;
01105 }
01106 
01107 //======================================================================
01108 
01109 
01110 /*
01111 ==================
01112 SetClientViewAngle
01113 
01114 ==================
01115 */
01116 void SetClientViewAngle( gentity_t *ent, vec3_t angle ) {
01117         int                     i;
01118 
01119         // set the delta angle
01120         for (i=0 ; i<3 ; i++) {
01121                 int             cmdAngle;
01122 
01123                 cmdAngle = ANGLE2SHORT(angle[i]);
01124                 ent->client->ps.delta_angles[i] = cmdAngle - ent->client->pers.cmd.angles[i];
01125         }
01126         VectorCopy( angle, ent->s.angles );
01127         VectorCopy (ent->s.angles, ent->client->ps.viewangles);
01128 }
01129 
01130 void MaintainBodyQueue(gentity_t *ent)
01131 { //do whatever should be done taking ragdoll and dismemberment states into account.
01132         qboolean doRCG = qfalse;
01133 
01134         assert(ent && ent->client);
01135         if (ent->client->tempSpectate > level.time ||
01136                 (ent->client->ps.eFlags2 & EF2_SHIP_DEATH))
01137         {
01138                 ent->client->noCorpse = qtrue;
01139         }
01140 
01141         if (!ent->client->noCorpse && !ent->client->ps.fallingToDeath)
01142         {
01143                 if (!CopyToBodyQue (ent))
01144                 {
01145                         doRCG = qtrue;
01146                 }
01147         }
01148         else
01149         {
01150                 ent->client->noCorpse = qfalse; //clear it for next time
01151                 ent->client->ps.fallingToDeath = qfalse;
01152                 doRCG = qtrue;
01153         }
01154 
01155         if (doRCG)
01156         { //bodyque func didn't manage to call ircg so call this to assure our limbs and ragdoll states are proper on the client.
01157                 trap_SendServerCommand(-1, va("rcg %i", ent->s.clientNum));
01158         }
01159 }
01160 
01161 /*
01162 ================
01163 respawn
01164 ================
01165 */
01166 void SiegeRespawn(gentity_t *ent);
01167 void respawn( gentity_t *ent ) {
01168         MaintainBodyQueue(ent);
01169 
01170         if (gEscaping || g_gametype.integer == GT_POWERDUEL)
01171         {
01172                 ent->client->sess.sessionTeam = TEAM_SPECTATOR;
01173                 ent->client->sess.spectatorState = SPECTATOR_FREE;
01174                 ent->client->sess.spectatorClient = 0;
01175 
01176                 ent->client->pers.teamState.state = TEAM_BEGIN;
01177                 ent->client->sess.spectatorTime = level.time;
01178                 ClientSpawn(ent);
01179                 ent->client->iAmALoser = qtrue;
01180                 return;
01181         }
01182 
01183         trap_UnlinkEntity (ent);
01184 
01185         if (g_gametype.integer == GT_SIEGE)
01186         {
01187                 if (g_siegeRespawn.integer)
01188                 {
01189                         if (ent->client->tempSpectate <= level.time)
01190                         {
01191                                 int minDel = g_siegeRespawn.integer* 2000;
01192                                 if (minDel < 20000)
01193                                 {
01194                                         minDel = 20000;
01195                                 }
01196                                 ent->client->tempSpectate = level.time + minDel;
01197                                 ent->health = ent->client->ps.stats[STAT_HEALTH] = 1;
01198                                 ent->client->ps.weapon = WP_NONE;
01199                                 ent->client->ps.stats[STAT_WEAPONS] = 0;
01200                                 ent->client->ps.stats[STAT_HOLDABLE_ITEMS] = 0;
01201                                 ent->client->ps.stats[STAT_HOLDABLE_ITEM] = 0;
01202                                 ent->takedamage = qfalse;
01203                                 trap_LinkEntity(ent);
01204 
01205                                 // Respawn time.
01206                                 if ( ent->s.number < MAX_CLIENTS )
01207                                 {
01208                                         gentity_t *te = G_TempEntity( ent->client->ps.origin, EV_SIEGESPEC );
01209                                         te->s.time = g_siegeRespawnCheck;
01210                                         te->s.owner = ent->s.number;
01211                                 }
01212 
01213                                 return;
01214                         }
01215                 }
01216                 SiegeRespawn(ent);
01217         }
01218         else
01219         {
01220                 gentity_t       *tent;
01221 
01222                 ClientSpawn(ent);
01223 
01224                 // add a teleportation effect
01225                 tent = G_TempEntity( ent->client->ps.origin, EV_PLAYER_TELEPORT_IN );
01226                 tent->s.clientNum = ent->s.clientNum;
01227         }
01228 }
01229 
01230 /*
01231 ================
01232 TeamCount
01233 
01234 Returns number of players on a team
01235 ================
01236 */
01237 team_t TeamCount( int ignoreClientNum, int team ) {
01238         int             i;
01239         int             count = 0;
01240 
01241         for ( i = 0 ; i < level.maxclients ; i++ ) {
01242                 if ( i == ignoreClientNum ) {
01243                         continue;
01244                 }
01245                 if ( level.clients[i].pers.connected == CON_DISCONNECTED ) {
01246                         continue;
01247                 }
01248                 if ( level.clients[i].sess.sessionTeam == team ) {
01249                         count++;
01250                 }
01251                 else if (g_gametype.integer == GT_SIEGE &&
01252             level.clients[i].sess.siegeDesiredTeam == team)
01253                 {
01254                         count++;
01255                 }
01256         }
01257 
01258         return count;
01259 }
01260 
01261 /*
01262 ================
01263 TeamLeader
01264 
01265 Returns the client number of the team leader
01266 ================
01267 */
01268 int TeamLeader( int team ) {
01269         int             i;
01270 
01271         for ( i = 0 ; i < level.maxclients ; i++ ) {
01272                 if ( level.clients[i].pers.connected == CON_DISCONNECTED ) {
01273                         continue;
01274                 }
01275                 if ( level.clients[i].sess.sessionTeam == team ) {
01276                         if ( level.clients[i].sess.teamLeader )
01277                                 return i;
01278                 }
01279         }
01280 
01281         return -1;
01282 }
01283 
01284 
01285 /*
01286 ================
01287 PickTeam
01288 
01289 ================
01290 */
01291 team_t PickTeam( int ignoreClientNum ) {
01292         int             counts[TEAM_NUM_TEAMS];
01293 
01294         counts[TEAM_BLUE] = TeamCount( ignoreClientNum, TEAM_BLUE );
01295         counts[TEAM_RED] = TeamCount( ignoreClientNum, TEAM_RED );
01296 
01297         if ( counts[TEAM_BLUE] > counts[TEAM_RED] ) {
01298