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                 return TEAM_RED;
01299         }
01300         if ( counts[TEAM_RED] > counts[TEAM_BLUE] ) {
01301                 return TEAM_BLUE;
01302         }
01303         // equal team count, so join the team with the lowest score
01304         if ( level.teamScores[TEAM_BLUE] > level.teamScores[TEAM_RED] ) {
01305                 return TEAM_RED;
01306         }
01307         return TEAM_BLUE;
01308 }
01309 
01310 /*
01311 ===========
01312 ForceClientSkin
01313 
01314 Forces a client's skin (for teamplay)
01315 ===========
01316 */
01317 /*
01318 static void ForceClientSkin( gclient_t *client, char *model, const char *skin ) {
01319         char *p;
01320 
01321         if ((p = Q_strrchr(model, '/')) != 0) {
01322                 *p = 0;
01323         }
01324 
01325         Q_strcat(model, MAX_QPATH, "/");
01326         Q_strcat(model, MAX_QPATH, skin);
01327 }
01328 */
01329 
01330 /*
01331 ===========
01332 ClientCheckName
01333 ============
01334 */
01335 static void ClientCleanName( const char *in, char *out, int outSize ) {
01336         int             len, colorlessLen;
01337         char    ch;
01338         char    *p;
01339         int             spaces;
01340 
01341         //save room for trailing null byte
01342         outSize--;
01343 
01344         len = 0;
01345         colorlessLen = 0;
01346         p = out;
01347         *p = 0;
01348         spaces = 0;
01349 
01350         while( 1 ) {
01351                 ch = *in++;
01352                 if( !ch ) {
01353                         break;
01354                 }
01355 
01356                 // don't allow leading spaces
01357                 if( !*p && ch == ' ' ) {
01358                         continue;
01359                 }
01360 
01361                 // check colors
01362                 if( ch == Q_COLOR_ESCAPE ) {
01363                         // solo trailing carat is not a color prefix
01364                         if( !*in ) {
01365                                 break;
01366                         }
01367 
01368                         // don't allow black in a name, period
01369                         if( ColorIndex(*in) == 0 ) {
01370                                 in++;
01371                                 continue;
01372                         }
01373 
01374                         // make sure room in dest for both chars
01375                         if( len > outSize - 2 ) {
01376                                 break;
01377                         }
01378 
01379                         *out++ = ch;
01380                         *out++ = *in++;
01381                         len += 2;
01382                         continue;
01383                 }
01384 
01385                 // don't allow too many consecutive spaces
01386                 if( ch == ' ' ) {
01387                         spaces++;
01388                         if( spaces > 3 ) {
01389                                 continue;
01390                         }
01391                 }
01392                 else {
01393                         spaces = 0;
01394                 }
01395 
01396                 if( len > outSize - 1 ) {
01397                         break;
01398                 }
01399 
01400                 *out++ = ch;
01401                 colorlessLen++;
01402                 len++;
01403         }
01404         *out = 0;
01405 
01406         // don't allow empty names
01407         if( *p == 0 || colorlessLen == 0 ) {
01408                 Q_strncpyz( p, "Padawan", outSize );
01409         }
01410 }
01411 
01412 #ifdef _DEBUG
01413 void G_DebugWrite(const char *path, const char *text)
01414 {
01415         fileHandle_t f;
01416 
01417         trap_FS_FOpenFile( path, &f, FS_APPEND );
01418         trap_FS_Write(text, strlen(text), f);
01419         trap_FS_FCloseFile(f);
01420 }
01421 #endif
01422 
01423 qboolean G_SaberModelSetup(gentity_t *ent)
01424 {
01425         int i = 0;
01426         qboolean fallbackForSaber = qtrue;
01427 
01428         while (i < MAX_SABERS)
01429         {
01430                 if (ent->client->saber[i].model[0])
01431                 {
01432                         //first kill it off if we've already got it
01433                         if (ent->client->weaponGhoul2[i])
01434                         {
01435                                 trap_G2API_CleanGhoul2Models(&(ent->client->weaponGhoul2[i]));
01436                         }
01437                         trap_G2API_InitGhoul2Model(&ent->client->weaponGhoul2[i], ent->client->saber[i].model, 0, 0, -20, 0, 0);
01438 
01439                         if (ent->client->weaponGhoul2[i])
01440                         {
01441                                 int j = 0;
01442                                 char *tagName;
01443                                 int tagBolt;
01444 
01445                                 if (ent->client->saber[i].skin)
01446                                 {
01447                                         trap_G2API_SetSkin(ent->client->weaponGhoul2[i], 0, ent->client->saber[i].skin, ent->client->saber[i].skin);
01448                                 }
01449 
01450                                 if (ent->client->saber[i].saberFlags & SFL_BOLT_TO_WRIST)
01451                                 {
01452                                         trap_G2API_SetBoltInfo(ent->client->weaponGhoul2[i], 0, 3+i);
01453                                 }
01454                                 else
01455                                 { // bolt to right hand for 0, or left hand for 1
01456                                         trap_G2API_SetBoltInfo(ent->client->weaponGhoul2[i], 0, i);
01457                                 }
01458 
01459                                 //Add all the bolt points
01460                                 while (j < ent->client->saber[i].numBlades)
01461                                 {
01462                                         tagName = va("*blade%i", j+1);
01463                                         tagBolt = trap_G2API_AddBolt(ent->client->weaponGhoul2[i], 0, tagName);
01464 
01465                                         if (tagBolt == -1)
01466                                         {
01467                                                 if (j == 0)
01468                                                 { //guess this is an 0ldsk3wl saber
01469                                                         tagBolt = trap_G2API_AddBolt(ent->client->weaponGhoul2[i], 0, "*flash");
01470                                                         fallbackForSaber = qfalse;
01471                                                         break;
01472                                                 }
01473 
01474                                                 if (tagBolt == -1)
01475                                                 {
01476                                                         assert(0);
01477                                                         break;
01478 
01479                                                 }
01480                                         }
01481                                         j++;
01482 
01483                                         fallbackForSaber = qfalse; //got at least one custom saber so don't need default
01484                                 }
01485 
01486                                 //Copy it into the main instance
01487                                 trap_G2API_CopySpecificGhoul2Model(ent->client->weaponGhoul2[i], 0, ent->ghoul2, i+1); 
01488                         }
01489                 }
01490                 else
01491                 {
01492                         break;
01493                 }
01494 
01495                 i++;
01496         }
01497 
01498         return fallbackForSaber;
01499 }
01500 
01501 /*
01502 ===========
01503 SetupGameGhoul2Model
01504 
01505 There are two ghoul2 model instances per player (actually three).  One is on the clientinfo (the base for the client side 
01506 player, and copied for player spawns and for corpses).  One is attached to the centity itself, which is the model acutally 
01507 animated and rendered by the system.  The final is the game ghoul2 model.  This is animated by pmove on the server, and
01508 is used for determining where the lightsaber should be, and for per-poly collision tests.
01509 ===========
01510 */
01511 void *g2SaberInstance = NULL;
01512 
01513 #include "../namespace_begin.h"
01514 qboolean BG_IsValidCharacterModel(const char *modelName, const char *skinName);
01515 qboolean BG_ValidateSkinForTeam( const char *modelName, char *skinName, int team, float *colors );
01516 void BG_GetVehicleModelName(char *modelname);
01517 #include "../namespace_end.h"
01518 
01519 void SetupGameGhoul2Model(gentity_t *ent, char *modelname, char *skinName)
01520 {
01521         int handle;
01522         char            afilename[MAX_QPATH];
01523 #if 0
01524         char             *slash;
01525 #endif
01526         char            GLAName[MAX_QPATH];
01527         vec3_t  tempVec = {0,0,0};
01528 
01529         // First things first.  If this is a ghoul2 model, then let's make sure we demolish this first.
01530         if (ent->ghoul2 && trap_G2_HaveWeGhoul2Models(ent->ghoul2))
01531         {
01532                 trap_G2API_CleanGhoul2Models(&(ent->ghoul2));
01533         }
01534 
01535         //rww - just load the "standard" model for the server"
01536         if (!precachedKyle)
01537         {
01538                 int defSkin;
01539 
01540                 Com_sprintf( afilename, sizeof( afilename ), "models/players/kyle/model.glm" );
01541                 handle = trap_G2API_InitGhoul2Model(&precachedKyle, afilename, 0, 0, -20, 0, 0);
01542 
01543                 if (handle<0)
01544                 {
01545                         return;
01546                 }
01547 
01548                 defSkin = trap_R_RegisterSkin("models/players/kyle/model_default.skin");
01549                 trap_G2API_SetSkin(precachedKyle, 0, defSkin, defSkin);
01550         }
01551 
01552         if (precachedKyle && trap_G2_HaveWeGhoul2Models(precachedKyle))
01553         {
01554                 if (d_perPlayerGhoul2.integer || ent->s.number >= MAX_CLIENTS ||
01555                         G_PlayerHasCustomSkeleton(ent))
01556                 { //rww - allow option for perplayer models on server for collision and bolt stuff.
01557                         char modelFullPath[MAX_QPATH];
01558                         char truncModelName[MAX_QPATH];
01559                         char skin[MAX_QPATH];
01560                         char vehicleName[MAX_QPATH];
01561                         int skinHandle = 0;
01562                         int i = 0;
01563                         char *p;
01564 
01565                         // If this is a vehicle, get it's model name.
01566                         if ( ent->client->NPC_class == CLASS_VEHICLE )
01567                         {
01568                                 strcpy(vehicleName, modelname);
01569                                 BG_GetVehicleModelName(modelname);
01570                                 strcpy(truncModelName, modelname);
01571                                 skin[0] = 0;
01572                                 if ( ent->m_pVehicle
01573                                         && ent->m_pVehicle->m_pVehicleInfo
01574                                         && ent->m_pVehicle->m_pVehicleInfo->skin
01575                                         && ent->m_pVehicle->m_pVehicleInfo->skin[0] )
01576                                 {
01577                                         skinHandle = trap_R_RegisterSkin(va("models/players/%s/model_%s.skin", modelname, ent->m_pVehicle->m_pVehicleInfo->skin));
01578                                 }
01579                                 else
01580                                 {
01581                                         skinHandle = trap_R_RegisterSkin(va("models/players/%s/model_default.skin", modelname));
01582                                 }
01583                         }
01584                         else
01585                         {
01586                                 if (skinName && skinName[0])
01587                                 {
01588                                         strcpy(skin, skinName);
01589                                         strcpy(truncModelName, modelname);
01590                                 }
01591                                 else
01592                                 {
01593                                         strcpy(skin, "default");
01594 
01595                                         strcpy(truncModelName, modelname);
01596                                         p = Q_strrchr(truncModelName, '/');
01597 
01598                                         if (p)
01599                                         {
01600                                                 *p = 0;
01601                                                 p++;
01602 
01603                                                 while (p && *p)
01604                                                 {
01605                                                         skin[i] = *p;
01606                                                         i++;
01607                                                         p++;
01608                                                 }
01609                                                 skin[i] = 0;
01610                                                 i = 0;
01611                                         }
01612 
01613                                         if (!BG_IsValidCharacterModel(truncModelName, skin))
01614                                         {
01615                                                 strcpy(truncModelName, "kyle");
01616                                                 strcpy(skin, "default");
01617                                         }
01618 
01619                                         if ( g_gametype.integer >= GT_TEAM && g_gametype.integer != GT_SIEGE && !g_trueJedi.integer )
01620                                         {
01621                                                 BG_ValidateSkinForTeam( truncModelName, skin, ent->client->sess.sessionTeam, NULL );
01622                                         }
01623                                         else if (g_gametype.integer == GT_SIEGE)
01624                                         { //force skin for class if appropriate
01625                                                 if (ent->client->siegeClass != -1)
01626                                                 {
01627                                                         siegeClass_t *scl = &bgSiegeClasses[ent->client->siegeClass];
01628                                                         if (scl->forcedSkin[0])
01629                                                         {
01630                                                                 strcpy(skin, scl->forcedSkin);
01631                                                         }
01632                                                 }
01633                                         }
01634                                 }
01635                         }
01636 
01637                         if (skin[0])
01638                         {
01639                                 char *useSkinName;
01640 
01641                                 if (strchr(skin, '|'))
01642                                 {//three part skin
01643                                         useSkinName = va("models/players/%s/|%s", truncModelName, skin);
01644                                 }
01645                                 else
01646                                 {
01647                                         useSkinName = va("models/players/%s/model_%s.skin", truncModelName, skin);
01648                                 }
01649 
01650                                 skinHandle = trap_R_RegisterSkin(useSkinName);
01651                         }
01652 
01653                         strcpy(modelFullPath, va("models/players/%s/model.glm", truncModelName));
01654                         handle = trap_G2API_InitGhoul2Model(&ent->ghoul2, modelFullPath, 0, skinHandle, -20, 0, 0);
01655 
01656                         if (handle<0)
01657                         { //Huh. Guess we don't have this model. Use the default.
01658 
01659                                 if (ent->ghoul2 && trap_G2_HaveWeGhoul2Models(ent->ghoul2))
01660                                 {
01661                                         trap_G2API_CleanGhoul2Models(&(ent->ghoul2));
01662                                 }
01663                                 ent->ghoul2 = NULL;
01664                                 trap_G2API_DuplicateGhoul2Instance(precachedKyle, &ent->ghoul2);
01665                         }
01666                         else
01667                         {
01668                                 trap_G2API_SetSkin(ent->ghoul2, 0, skinHandle, skinHandle);
01669 
01670                                 GLAName[0] = 0;
01671                                 trap_G2API_GetGLAName( ent->ghoul2, 0, GLAName);
01672 
01673                                 if (!GLAName[0] || (!strstr(GLAName, "players/_humanoid/") && ent->s.number < MAX_CLIENTS && !G_PlayerHasCustomSkeleton(ent)))
01674                                 { //a bad model
01675                                         trap_G2API_CleanGhoul2Models(&(ent->ghoul2));
01676                                         ent->ghoul2 = NULL;
01677                                         trap_G2API_DuplicateGhoul2Instance(precachedKyle, &ent->ghoul2);
01678                                 }
01679 
01680                                 if (ent->s.number >= MAX_CLIENTS)
01681                                 {
01682                                         ent->s.modelGhoul2 = 1; //so we know to free it on the client when we're removed.
01683 
01684                                         if (skin[0])
01685                                         { //append it after a *
01686                                                 strcat( modelFullPath, va("*%s", skin) );
01687                                         }
01688 
01689                                         if ( ent->client->NPC_class == CLASS_VEHICLE )
01690                                         { //vehicles are tricky and send over their vehicle names as the model (the model is then retrieved based on the vehicle name)
01691                                                 ent->s.modelindex = G_ModelIndex(vehicleName);
01692                                         }
01693                                         else
01694                                         {
01695                                                 ent->s.modelindex = G_ModelIndex(modelFullPath);
01696                                         }
01697                                 }
01698                         }
01699                 }
01700                 else
01701                 {
01702                         trap_G2API_DuplicateGhoul2Instance(precachedKyle, &ent->ghoul2);
01703                 }
01704         }
01705         else
01706         {
01707                 return;
01708         }
01709 
01710         //Attach the instance to this entity num so we can make use of client-server
01711         //shared operations if possible.
01712         trap_G2API_AttachInstanceToEntNum(ent->ghoul2, ent->s.number, qtrue);
01713 
01714         // The model is now loaded.
01715 
01716         GLAName[0] = 0;
01717 
01718         if (!BGPAFtextLoaded)
01719         {
01720                 if (BG_ParseAnimationFile("models/players/_humanoid/animation.cfg", bgHumanoidAnimations, qtrue) == -1)
01721                 {
01722                         Com_Printf( "Failed to load humanoid animation file\n");
01723                         return;
01724                 }
01725         }
01726 
01727         if (ent->s.number >= MAX_CLIENTS || G_PlayerHasCustomSkeleton(ent))
01728         {
01729                 ent->localAnimIndex = -1;
01730 
01731                 GLAName[0] = 0;
01732                 trap_G2API_GetGLAName(ent->ghoul2, 0, GLAName);
01733 
01734                 if (GLAName[0] &&
01735                         !strstr(GLAName, "players/_humanoid/") /*&&
01736                         !strstr(GLAName, "players/rockettrooper/")*/)
01737                 { //it doesn't use humanoid anims.
01738                         char *slash = Q_strrchr( GLAName, '/' );
01739                         if ( slash )
01740                         {
01741                                 strcpy(slash, "/animation.cfg");
01742 
01743                                 ent->localAnimIndex = BG_ParseAnimationFile(GLAName, NULL, qfalse);
01744                         }
01745                 }
01746                 else
01747                 { //humanoid index.
01748                         if (strstr(GLAName, "players/rockettrooper/"))
01749                         {
01750                                 ent->localAnimIndex = 1;
01751                         }
01752                         else
01753                         {
01754                                 ent->localAnimIndex = 0;
01755                         }
01756                 }
01757 
01758                 if (ent->localAnimIndex == -1)
01759                 {
01760                         Com_Error(ERR_DROP, "NPC had an invalid GLA\n");
01761                 }
01762         }
01763         else
01764         {
01765                 GLAName[0] = 0;
01766                 trap_G2API_GetGLAName(ent->ghoul2, 0, GLAName);
01767 
01768                 if (strstr(GLAName, "players/rockettrooper/"))
01769                 {
01770                         //assert(!"Should not have gotten in here with rockettrooper skel");
01771                         ent->localAnimIndex = 1;
01772                 }
01773                 else
01774                 {
01775                         ent->localAnimIndex = 0;
01776                 }
01777         }
01778 
01779         if (ent->s.NPC_class == CLASS_VEHICLE &&
01780                 ent->m_pVehicle)
01781         { //do special vehicle stuff
01782                 char strTemp[128];
01783                 int i;
01784 
01785                 // Setup the default first bolt
01786                 i = trap_G2API_AddBolt( ent->ghoul2, 0, "model_root" );
01787 
01788                 // Setup the droid unit.
01789                 ent->m_pVehicle->m_iDroidUnitTag = trap_G2API_AddBolt( ent->ghoul2, 0, "*droidunit" );
01790 
01791                 // Setup the Exhausts.
01792                 for ( i = 0; i < MAX_VEHICLE_EXHAUSTS; i++ )
01793                 {
01794                         Com_sprintf( strTemp, 128, "*exhaust%i", i + 1 );
01795                         ent->m_pVehicle->m_iExhaustTag[i] = trap_G2API_AddBolt( ent->ghoul2, 0, strTemp );
01796                 }
01797 
01798                 // Setup the Muzzles.
01799                 for ( i = 0; i < MAX_VEHICLE_MUZZLES; i++ )
01800                 {
01801                         Com_sprintf( strTemp, 128, "*muzzle%i", i + 1 );
01802                         ent->m_pVehicle->m_iMuzzleTag[i] = trap_G2API_AddBolt( ent->ghoul2, 0, strTemp );
01803                         if ( ent->m_pVehicle->m_iMuzzleTag[i] == -1 )
01804                         {//ergh, try *flash?
01805                                 Com_sprintf( strTemp, 128, "*flash%i", i + 1 );
01806                                 ent->m_pVehicle->m_iMuzzleTag[i] = trap_G2API_AddBolt( ent->ghoul2, 0, strTemp );
01807                         }
01808                 }
01809 
01810                 // Setup the Turrets.
01811                 for ( i = 0; i < MAX_VEHICLE_TURRET_MUZZLES; i++ )
01812                 {
01813                         if ( ent->m_pVehicle->m_pVehicleInfo->turret[i].gunnerViewTag )
01814                         {
01815                                 ent->m_pVehicle->m_iGunnerViewTag[i] = trap_G2API_AddBolt( ent->ghoul2, 0, ent->m_pVehicle->m_pVehicleInfo->turret[i].gunnerViewTag );
01816                         }
01817                         else
01818                         {
01819                                 ent->m_pVehicle->m_iGunnerViewTag[i] = -1;
01820                         }
01821                 }
01822         }
01823         
01824         if (ent->client->ps.weapon == WP_SABER || ent->s.number < MAX_CLIENTS)
01825         { //a player or NPC saber user
01826                 trap_G2API_AddBolt(ent->ghoul2, 0, "*r_hand");
01827                 trap_G2API_AddBolt(ent->ghoul2, 0, "*l_hand");
01828 
01829                 //rhand must always be first bolt. lhand always second. Whichever you want the
01830                 //jetpack bolted to must always be third.
01831                 trap_G2API_AddBolt(ent->ghoul2, 0, "*chestg");
01832 
01833                 //claw bolts
01834                 trap_G2API_AddBolt(ent->ghoul2, 0, "*r_hand_cap_r_arm");
01835                 trap_G2API_AddBolt(ent->ghoul2, 0, "*l_hand_cap_l_arm");
01836 
01837                 trap_G2API_SetBoneAnim(ent->ghoul2, 0, "model_root", 0, 12, BONE_ANIM_OVERRIDE_LOOP, 1.0f, level.time, -1, -1);
01838                 trap_G2API_SetBoneAngles(ent->ghoul2, 0, "upper_lumbar", tempVec, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL, 0, level.time);
01839                 trap_G2API_SetBoneAngles(ent->ghoul2, 0, "cranium", tempVec, BONE_ANGLES_POSTMULT, POSITIVE_Z, NEGATIVE_Y, POSITIVE_X, NULL, 0, level.time);
01840 
01841                 if (!g2SaberInstance)
01842                 {
01843                         trap_G2API_InitGhoul2Model(&g2SaberInstance, "models/weapons2/saber/saber_w.glm", 0, 0, -20, 0, 0);
01844 
01845                         if (g2SaberInstance)
01846                         {
01847                                 // indicate we will be bolted to model 0 (ie the player) on bolt 0 (always the right hand) when we get copied
01848                                 trap_G2API_SetBoltInfo(g2SaberInstance, 0, 0);
01849                                 // now set up the gun bolt on it
01850                                 trap_G2API_AddBolt(g2SaberInstance, 0, "*blade1");
01851                         }
01852                 }
01853 
01854                 if (G_SaberModelSetup(ent))
01855                 {
01856                         if (g2SaberInstance)
01857                         {
01858                                 trap_G2API_CopySpecificGhoul2Model(g2SaberInstance, 0, ent->ghoul2, 1); 
01859                         }
01860                 }
01861         }
01862 
01863         if (ent->s.number >= MAX_CLIENTS)
01864         { //some extra NPC stuff
01865                 if (trap_G2API_AddBolt(ent->ghoul2, 0, "lower_lumbar") == -1)
01866                 { //check now to see if we have this bone for setting anims and such
01867                         ent->noLumbar = qtrue;
01868                 }
01869         }
01870 }
01871 
01872 
01873 
01874 
01875 /*
01876 ===========
01877 ClientUserInfoChanged
01878 
01879 Called from ClientConnect when the player first connects and
01880 directly by the server system when the player updates a userinfo variable.
01881 
01882 The game can override any of the settings and call trap_SetUserinfo
01883 if desired.
01884 ============
01885 */
01886 qboolean G_SetSaber(gentity_t *ent, int saberNum, char *saberName, qboolean siegeOverride);
01887 void G_ValidateSiegeClassForTeam(gentity_t *ent, int team);
01888 void ClientUserinfoChanged( int clientNum ) {
01889         gentity_t *ent;
01890         int             teamTask, teamLeader, team, health;
01891         char    *s;
01892         char    model[MAX_QPATH];
01893         //char  headModel[MAX_QPATH];
01894         char    forcePowers[MAX_QPATH];
01895         char    oldname[MAX_STRING_CHARS];
01896         gclient_t       *client;
01897         char    c1[MAX_INFO_STRING];
01898         char    c2[MAX_INFO_STRING];
01899 //      char    redTeam[MAX_INFO_STRING];
01900 //      char    blueTeam[MAX_INFO_STRING];
01901         char    userinfo[MAX_INFO_STRING];
01902         char    className[MAX_QPATH]; //name of class type to use in siege
01903         char    saberName[MAX_QPATH];
01904         char    saber2Name[MAX_QPATH];
01905         char    *value;
01906         int             maxHealth;
01907         qboolean        modelChanged = qfalse;
01908 
01909         ent = g_entities + clientNum;
01910         client = ent->client;
01911 
01912         trap_GetUserinfo( clientNum, userinfo, sizeof( userinfo ) );
01913 
01914         // check for malformed or illegal info strings
01915         if ( !Info_Validate(userinfo) ) {
01916                 strcpy (userinfo, "\\name\\badinfo");
01917         }
01918 
01919         // check for local client
01920         s = Info_ValueForKey( userinfo, "ip" );
01921         if ( !strcmp( s, "localhost" ) ) {
01922                 client->pers.localClient = qtrue;
01923         }
01924 
01925         // check the item prediction
01926         s = Info_ValueForKey( userinfo, "cg_predictItems" );
01927         if ( !atoi( s ) ) {
01928                 client->pers.predictItemPickup = qfalse;
01929         } else {
01930                 client->pers.predictItemPickup = qtrue;
01931         }
01932 
01933         // set name
01934         Q_strncpyz ( oldname, client->pers.netname, sizeof( oldname ) );
01935         s = Info_ValueForKey (userinfo, "name");
01936         ClientCleanName( s, client->pers.netname, sizeof(client->pers.netname) );
01937 
01938         if ( client->sess.sessionTeam == TEAM_SPECTATOR ) {
01939                 if ( client->sess.spectatorState == SPECTATOR_SCOREBOARD ) {
01940                         Q_strncpyz( client->pers.netname, "scoreboard", sizeof(client->pers.netname) );
01941                 }
01942         }
01943 
01944         if ( client->pers.connected == CON_CONNECTED ) {
01945                 if ( strcmp( oldname, client->pers.netname ) ) 
01946                 {
01947                         if ( client->pers.netnameTime > level.time  )
01948                         {
01949                                 trap_SendServerCommand( clientNum, va("print \"%s\n\"", G_GetStringEdString("MP_SVGAME", "NONAMECHANGE")) );
01950 
01951                                 Info_SetValueForKey( userinfo, "name", oldname );
01952                                 trap_SetUserinfo( clientNum, userinfo );                        
01953                                 strcpy ( client->pers.netname, oldname );
01954                         }
01955                         else
01956                         {                               
01957                                 trap_SendServerCommand( -1, va("print \"%s" S_COLOR_WHITE " %s %s\n\"", oldname, G_GetStringEdString("MP_SVGAME", "PLRENAME"), client->pers.netname) );
01958                                 client->pers.netnameTime = level.time + 5000;
01959                         }
01960                 }
01961         }
01962 
01963         // set model
01964         Q_strncpyz( model, Info_ValueForKey (userinfo, "model"), sizeof( model ) );
01965 
01966         if (d_perPlayerGhoul2.integer)
01967         {
01968                 if (Q_stricmp(model, client->modelname))
01969                 {
01970                         strcpy(client->modelname, model);
01971                         modelChanged = qtrue;
01972                 }
01973         }
01974 
01975         //Get the skin RGB based on his userinfo
01976         value = Info_ValueForKey (userinfo, "char_color_red");
01977         if (value)
01978         {
01979                 client->ps.customRGBA[0] = atoi(value);
01980         }
01981         else
01982         {
01983                 client->ps.customRGBA[0] = 255;
01984         }
01985 
01986         value = Info_ValueForKey (userinfo, "char_color_green");
01987         if (value)
01988         {
01989                 client->ps.customRGBA[1] = atoi(value);
01990         }
01991         else
01992         {
01993                 client->ps.customRGBA[1] = 255;
01994         }
01995 
01996         value = Info_ValueForKey (userinfo, "char_color_blue");
01997         if (value)
01998         {
01999                 client->ps.customRGBA[2] = atoi(value);
02000         }
02001         else
02002         {
02003                 client->ps.customRGBA[2] = 255;
02004         }
02005 
02006         if ((client->ps.customRGBA[0]+client->ps.customRGBA[1]+client->ps.customRGBA[2]) < 100)
02007         { //hmm, too dark!
02008                 client->ps.customRGBA[0] = client->ps.customRGBA[1] = client->ps.customRGBA[2] = 255;
02009         }
02010 
02011         client->ps.customRGBA[3]=255;
02012 
02013         Q_strncpyz( forcePowers, Info_ValueForKey (userinfo, "forcepowers"), sizeof( forcePowers ) );
02014 
02015         // bots set their team a few frames later
02016         if (g_gametype.integer >= GT_TEAM && g_entities[clientNum].r.svFlags & SVF_BOT) {
02017                 s = Info_ValueForKey( userinfo, "team" );
02018                 if ( !Q_stricmp( s, "red" ) || !Q_stricmp( s, "r" ) ) {
02019                         team = TEAM_RED;
02020                 } else if ( !Q_stricmp( s, "blue" ) || !Q_stricmp( s, "b" ) ) {
02021                         team = TEAM_BLUE;
02022                 } else {
02023                         // pick the team with the least number of players
02024                         team = PickTeam( clientNum );
02025                 }
02026         }
02027         else {
02028                 team = client->sess.sessionTeam;
02029         }
02030 
02031         //Set the siege class
02032         if (g_gametype.integer == GT_SIEGE)
02033         {
02034                 strcpy(className, client->sess.siegeClass);
02035 
02036                 //This function will see if the given class is legal for the given team.
02037                 //If not className will be filled in with the first legal class for this team.
02038 /*              if (!BG_SiegeCheckClassLegality(team, className) &&
02039                         Q_stricmp(client->sess.siegeClass, "none"))
02040                 { //if it isn't legal pop up the class menu
02041                         trap_SendServerCommand(ent-g_entities, "scl");
02042                 }
02043 */
02044                 //Now that the team is legal for sure, we'll go ahead and get an index for it.
02045                 client->siegeClass = BG_SiegeFindClassIndexByName(className);
02046                 if (client->siegeClass == -1)
02047                 { //ok, get the first valid class for the team you're on then, I guess.
02048                         BG_SiegeCheckClassLegality(team, className);
02049                         strcpy(client->sess.siegeClass, className);
02050                         client->siegeClass = BG_SiegeFindClassIndexByName(className);
02051                 }
02052                 else
02053                 { //otherwise, make sure the class we are using is legal.
02054                         G_ValidateSiegeClassForTeam(ent, team);
02055                         strcpy(className, client->sess.siegeClass);
02056                 }
02057 
02058                 //Set the sabers if the class dictates
02059                 if (client->siegeClass != -1)
02060                 {
02061                         siegeClass_t *scl = &bgSiegeClasses[client->siegeClass];
02062 
02063                         if (scl->saber1[0])
02064                         {
02065                                 G_SetSaber(ent, 0, scl->saber1, qtrue);
02066                         }
02067                         else
02068                         { //default I guess
02069                                 G_SetSaber(ent, 0, "Kyle", qtrue);
02070                         }
02071                         if (scl->saber2[0])
02072                         {
02073                                 G_SetSaber(ent, 1, scl->saber2, qtrue);
02074                         }
02075                         else
02076                         { //no second saber then
02077                                 G_SetSaber(ent, 1, "none", qtrue);
02078                         }
02079 
02080                         //make sure the saber models are updated
02081                         G_SaberModelSetup(ent);
02082 
02083                         if (scl->forcedModel[0])
02084                         { //be sure to override the model we actually use
02085                                 strcpy(model, scl->forcedModel);
02086                                 if (d_perPlayerGhoul2.integer)
02087                                 {
02088                                         if (Q_stricmp(model, client->modelname))
02089                                         {
02090                                                 strcpy(client->modelname, model);
02091                                                 modelChanged = qtrue;
02092                                         }
02093                                 }
02094                         }
02095 
02096                         //force them to use their class model on the server, if the class dictates
02097                         if (G_PlayerHasCustomSkeleton(ent))
02098                         {
02099                                 if (Q_stricmp(model, client->modelname) || ent->localAnimIndex == 0)
02100                                 {
02101                                         strcpy(client->modelname, model);
02102                                         modelChanged = qtrue;
02103                                 }
02104                         }
02105                 }
02106         }
02107         else
02108         {
02109                 strcpy(className, "none");
02110         }
02111 
02112         //Set the saber name
02113         strcpy(saberName, client->sess.saberType);
02114         strcpy(saber2Name, client->sess.saber2Type);
02115 
02116         // set max health
02117         if (g_gametype.integer == GT_SIEGE && client->siegeClass != -1)
02118         {
02119                 siegeClass_t *scl = &bgSiegeClasses[client->siegeClass];
02120                 maxHealth = 100;
02121 
02122                 if (scl->maxhealth)
02123                 {
02124                         maxHealth = scl->maxhealth;
02125                 }
02126 
02127                 health = maxHealth;
02128         }
02129         else
02130         {
02131                 maxHealth = 100;
02132                 health = 100; //atoi( Info_ValueForKey( userinfo, "handicap" ) );
02133         }
02134         client->pers.maxHealth = health;
02135         if ( client->pers.maxHealth < 1 || client->pers.maxHealth > maxHealth ) {
02136                 client->pers.maxHealth = 100;
02137         }
02138         client->ps.stats[STAT_MAX_HEALTH] = client->pers.maxHealth;
02139 
02140 /*      NOTE: all client side now
02141 
02142         // team
02143         switch( team ) {
02144         case TEAM_RED:
02145                 ForceClientSkin(client, model, "red");
02146 //              ForceClientSkin(client, headModel, "red");
02147                 break;
02148         case TEAM_BLUE:
02149                 ForceClientSkin(client, model, "blue");
02150 //              ForceClientSkin(client, headModel, "blue");
02151                 break;
02152         }
02153         // don't ever use a default skin in teamplay, it would just waste memory
02154         // however bots will always join a team but they spawn in as spectator
02155         if ( g_gametype.integer >= GT_TEAM && team == TEAM_SPECTATOR) {
02156                 ForceClientSkin(client, model, "red");
02157 //              ForceClientSkin(client, headModel, "red");
02158         }
02159 */
02160 
02161         if (g_gametype.integer >= GT_TEAM) {
02162                 client->pers.teamInfo = qtrue;
02163         } else {
02164                 s = Info_ValueForKey( userinfo, "teamoverlay" );
02165                 if ( ! *s || atoi( s ) != 0 ) {
02166                         client->pers.teamInfo = qtrue;
02167                 } else {
02168                         client->pers.teamInfo = qfalse;
02169                 }
02170         }
02171         /*
02172         s = Info_ValueForKey( userinfo, "cg_pmove_fixed" );
02173         if ( !*s || atoi( s ) == 0 ) {
02174                 client->pers.pmoveFixed = qfalse;
02175         }
02176         else {
02177                 client->pers.pmoveFixed = qtrue;
02178         }
02179         */
02180 
02181         // team task (0 = none, 1 = offence, 2 = defence)
02182         teamTask = atoi(Info_ValueForKey(userinfo, "teamtask"));
02183         // team Leader (1 = leader, 0 is normal player)
02184         teamLeader = client->sess.teamLeader;
02185 
02186         // colors
02187         strcpy(c1, Info_ValueForKey( userinfo, "color1" ));
02188         strcpy(c2, Info_ValueForKey( userinfo, "color2" ));
02189 
02190 //      strcpy(redTeam, Info_ValueForKey( userinfo, "g_redteam" ));
02191 //      strcpy(blueTeam, Info_ValueForKey( userinfo, "g_blueteam" ));
02192 
02193         // send over a subset of the userinfo keys so other clients can
02194         // print scoreboards, display models, and play custom sounds
02195         if ( ent->r.svFlags & SVF_BOT ) {
02196                 s = va("n\\%s\\t\\%i\\model\\%s\\c1\\%s\\c2\\%s\\hc\\%i\\w\\%i\\l\\%i\\skill\\%s\\tt\\%d\\tl\\%d\\siegeclass\\%s\\st\\%s\\st2\\%s\\dt\\%i\\sdt\\%i",
02197                         client->pers.netname, team, model,  c1, c2, 
02198                         client->pers.maxHealth, client->sess.wins, client->sess.losses,
02199                         Info_ValueForKey( userinfo, "skill" ), teamTask, teamLeader, className, saberName, saber2Name, client->sess.duelTeam, client->sess.siegeDesiredTeam );
02200         } else {
02201                 if (g_gametype.integer == GT_SIEGE)
02202                 { //more crap to send
02203                         s = va("n\\%s\\t\\%i\\model\\%s\\c1\\%s\\c2\\%s\\hc\\%i\\w\\%i\\l\\%i\\tt\\%d\\tl\\%d\\siegeclass\\%s\\st\\%s\\st2\\%s\\dt\\%i\\sdt\\%i",
02204                                 client->pers.netname, client->sess.sessionTeam, model, c1, c2, 
02205                                 client->pers.maxHealth, client->sess.wins, client->sess.losses, teamTask, teamLeader, className, saberName, saber2Name, client->sess.duelTeam, client->sess.siegeDesiredTeam);
02206                 }
02207                 else
02208                 {
02209                         s = va("n\\%s\\t\\%i\\model\\%s\\c1\\%s\\c2\\%s\\hc\\%i\\w\\%i\\l\\%i\\tt\\%d\\tl\\%d\\st\\%s\\st2\\%s\\dt\\%i",
02210                                 client->pers.netname, client->sess.sessionTeam, model, c1, c2, 
02211                                 client->pers.maxHealth, client->sess.wins, client->sess.losses, teamTask, teamLeader, saberName, saber2Name, client->sess.duelTeam);
02212                 }
02213         }
02214 
02215         trap_SetConfigstring( CS_PLAYERS+clientNum, s );
02216 
02217         if (modelChanged) //only going to be true for allowable server-side custom skeleton cases
02218         { //update the server g2 instance if appropriate
02219                 char *modelname = Info_ValueForKey (userinfo, "model");
02220                 SetupGameGhoul2Model(ent, modelname, NULL);
02221 
02222                 if (ent->ghoul2 && ent->client)
02223                 {
02224                         ent->client->renderInfo.lastG2 = NULL; //update the renderinfo bolts next update.
02225                 }
02226 
02227                 client->torsoAnimExecute = client->legsAnimExecute = -1;
02228                 client->torsoLastFlip = client->legsLastFlip = qfalse;
02229         }
02230 
02231         if (g_logClientInfo.integer)
02232         {
02233                 G_LogPrintf( "ClientUserinfoChanged: %i %s\n", clientNum, s );
02234         }
02235 }
02236 
02237 
02238 /*
02239 ===========
02240 ClientConnect
02241 
02242 Called when a player begins connecting to the server.
02243 Called again for every map change or tournement restart.
02244 
02245 The session information will be valid after exit.
02246 
02247 Return NULL if the client should be allowed, otherwise return
02248 a string with the reason for denial.
02249 
02250 Otherwise, the client will be sent the current gamestate
02251 and will eventually get to ClientBegin.
02252 
02253 firstTime will be qtrue the very first time a client connects
02254 to the server machine, but qfalse on map changes and tournement
02255 restarts.
02256 ============
02257 */
02258 char *ClientConnect( int clientNum, qboolean firstTime, qboolean isBot ) {
02259         char            *value;
02260 //      char            *areabits;
02261         gclient_t       *client;
02262         char            userinfo[MAX_INFO_STRING];
02263         gentity_t       *ent;
02264         gentity_t       *te;
02265 
02266         ent = &g_entities[ clientNum ];
02267 
02268         trap_GetUserinfo( clientNum, userinfo, sizeof( userinfo ) );
02269 
02270         // check to see if they are on the banned IP list
02271         value = Info_ValueForKey (userinfo, "ip");
02272         if ( G_FilterPacket( value ) ) {
02273                 return "Banned.";
02274         }
02275 
02276         if ( !( ent->r.svFlags & SVF_BOT ) && !isBot && g_needpass.integer ) {
02277                 // check for a password
02278                 value = Info_ValueForKey (userinfo, "password");
02279                 if ( g_password.string[0] && Q_stricmp( g_password.string, "none" ) &&
02280                         strcmp( g_password.string, value) != 0) {
02281                         static char sTemp[1024];
02282                         Q_strncpyz(sTemp, G_GetStringEdString("MP_SVGAME","INVALID_ESCAPE_TO_MAIN"), sizeof (sTemp) );
02283                         return sTemp;// return "Invalid password";
02284                 }
02285         }
02286 
02287         // they can connect
02288         ent->client = level.clients + clientNum;
02289         client = ent->client;
02290 
02291         //assign the pointer for bg entity access
02292         ent->playerState = &ent->client->ps;
02293 
02294 //      areabits = client->areabits;
02295 
02296         memset( client, 0, sizeof(*client) );
02297 
02298         client->pers.connected = CON_CONNECTING;
02299 
02300         // read or initialize the session data
02301         if ( firstTime || level.newSession ) {
02302                 G_InitSessionData( client, userinfo, isBot );
02303         }
02304         G_ReadSessionData( client );
02305 
02306         if (g_gametype.integer == GT_SIEGE &&
02307                 (firstTime || level.newSession))
02308         { //if this is the first time then auto-assign a desired siege team and show briefing for that team
02309                 client->sess.siegeDesiredTeam = 0;//PickTeam(ent->s.number);
02310                 /*
02311                 trap_SendServerCommand(ent->s.number, va("sb %i", client->sess.siegeDesiredTeam));
02312                 */
02313                 //don't just show it - they'll see it if they switch to a team on purpose.
02314         }
02315 
02316 
02317         if (g_gametype.integer == GT_SIEGE && client->sess.sessionTeam != TEAM_SPECTATOR)
02318         {
02319                 if (firstTime || level.newSession)
02320                 { //start as spec
02321                         client->sess.siegeDesiredTeam = client->sess.sessionTeam;
02322                         client->sess.sessionTeam = TEAM_SPECTATOR;
02323                 }
02324         }
02325         else if (g_gametype.integer == GT_POWERDUEL && client->sess.sessionTeam != TEAM_SPECTATOR)
02326         {
02327                 client->sess.sessionTeam = TEAM_SPECTATOR;
02328         }
02329 
02330         if( isBot ) {
02331                 ent->r.svFlags |= SVF_BOT;
02332                 ent->inuse = qtrue;
02333                 if( !G_BotConnect( clientNum, !firstTime ) ) {
02334                         return "BotConnectfailed";
02335                 }
02336         }
02337 
02338         // get and distribute relevent paramters
02339         G_LogPrintf( "ClientConnect: %i\n", clientNum );
02340         ClientUserinfoChanged( clientNum );
02341 
02342         // don't do the "xxx connected" messages if they were caried over from previous level
02343         if ( firstTime ) {
02344                 trap_SendServerCommand( -1, va("print \"%s" S_COLOR_WHITE " %s\n\"", client->pers.netname, G_GetStringEdString("MP_SVGAME", "PLCONNECT")) );
02345         }
02346 
02347         if ( g_gametype.integer >= GT_TEAM &&
02348                 client->sess.sessionTeam != TEAM_SPECTATOR ) {
02349                 BroadcastTeamChange( client, -1 );
02350         }
02351 
02352         // count current clients and rank for scoreboard
02353         CalculateRanks();
02354 
02355         te = G_TempEntity( vec3_origin, EV_CLIENTJOIN );
02356         te->r.svFlags |= SVF_BROADCAST;
02357         te->s.eventParm = clientNum;
02358 
02359         // for statistics
02360 //      client->areabits = areabits;
02361 //      if ( !client->areabits )
02362 //              client->areabits = G_Alloc( (trap_AAS_PointReachabilityAreaIndex( NULL ) + 7) / 8 );
02363 
02364         return NULL;
02365 }
02366 
02367 void G_WriteClientSessionData( gclient_t *client );
02368 
02369 #include "../namespace_begin.h"
02370 void WP_SetSaber( int entNum, saberInfo_t *sabers, int saberNum, const char *saberName );
02371 #include "../namespace_end.h"
02372 
02373 /*
02374 ===========
02375 ClientBegin
02376 
02377 called when a client has finished connecting, and is ready
02378 to be placed into the level.  This will happen every level load,
02379 and on transition between teams, but doesn't happen on respawns
02380 ============
02381 */
02382 extern qboolean gSiegeRoundBegun;
02383 extern qboolean gSiegeRoundEnded;
02384 void SetTeamQuick(gentity_t *ent, int team, qboolean doBegin);
02385 void ClientBegin( int clientNum, qboolean allowTeamReset ) {
02386         gentity_t       *ent;
02387         gclient_t       *client;
02388         gentity_t       *tent;
02389         int                     flags, i;
02390         char            userinfo[MAX_INFO_VALUE], *modelname;
02391 
02392         ent = g_entities + clientNum;
02393 
02394         if ((ent->r.svFlags & SVF_BOT) && g_gametype.integer >= GT_TEAM)
02395         {
02396                 if (allowTeamReset)
02397                 {
02398                         const char *team = "Red";
02399                         int preSess;
02400 
02401                         //SetTeam(ent, "");
02402                         ent->client->sess.sessionTeam = PickTeam(-1);
02403                         trap_GetUserinfo(clientNum, userinfo, MAX_INFO_STRING);
02404 
02405                         if (ent->client->sess.sessionTeam == TEAM_SPECTATOR)
02406                         {
02407                                 ent->client->sess.sessionTeam = TEAM_RED;
02408                         }
02409 
02410                         if (ent->client->sess.sessionTeam == TEAM_RED)
02411                         {
02412                                 team = "Red";
02413                         }
02414                         else
02415                         {
02416                                 team = "Blue";
02417                         }
02418 
02419                         Info_SetValueForKey( userinfo, "team", team );
02420 
02421                         trap_SetUserinfo( clientNum, userinfo );
02422 
02423                         ent->client->ps.persistant[ PERS_TEAM ] = ent->client->sess.sessionTeam;
02424 
02425                         preSess = ent->client->sess.sessionTeam;
02426                         G_ReadSessionData( ent->client );
02427                         ent->client->sess.sessionTeam = preSess;
02428                         G_WriteClientSessionData(ent->client);
02429                         ClientUserinfoChanged( clientNum );
02430                         ClientBegin(clientNum, qfalse);
02431                         return;
02432                 }
02433         }
02434 
02435         client = level.clients + clientNum;
02436 
02437         if ( ent->r.linked ) {
02438                 trap_UnlinkEntity( ent );
02439         }
02440         G_InitGentity( ent );
02441         ent->touch = 0;
02442         ent->pain = 0;
02443         ent->client = client;
02444 
02445         //assign the pointer for bg entity access
02446         ent->playerState = &ent->client->ps;
02447 
02448         client->pers.connected = CON_CONNECTED;
02449         client->pers.enterTime = level.time;
02450         client->pers.teamState.state = TEAM_BEGIN;
02451 
02452         // save eflags around this, because changing teams will
02453         // cause this to happen with a valid entity, and we
02454         // want to make sure the teleport bit is set right
02455         // so the viewpoint doesn't interpolate through the
02456         // world to the new position
02457         flags = client->ps.eFlags;
02458 
02459         i = 0;
02460 
02461         while (i < NUM_FORCE_POWERS)
02462         {
02463                 if (ent->client->ps.fd.forcePowersActive & (1 << i))
02464                 {
02465                         WP_ForcePowerStop(ent, i);
02466                 }
02467                 i++;
02468         }
02469 
02470         i = TRACK_CHANNEL_1;
02471 
02472         while (i < NUM_TRACK_CHANNELS)
02473         {
02474                 if (ent->client->ps.fd.killSoundEntIndex[i-50] && ent->client->ps.fd.killSoundEntIndex[i-50] < MAX_GENTITIES && ent->client->ps.fd.killSoundEntIndex[i-50] > 0)
02475                 {
02476                         G_MuteSound(ent->client->ps.fd.killSoundEntIndex[i-50], CHAN_VOICE);
02477                 }
02478                 i++;
02479         }
02480         i = 0;
02481 
02482         memset( &client->ps, 0, sizeof( client->ps ) );
02483         client->ps.eFlags = flags;
02484 
02485         client->ps.hasDetPackPlanted = qfalse;
02486 
02487         //first-time force power initialization
02488         WP_InitForcePowers( ent );
02489 
02490         //init saber ent
02491         WP_SaberInitBladeData( ent );
02492 
02493         // First time model setup for that player.
02494         trap_GetUserinfo( clientNum, userinfo, sizeof(userinfo) );
02495         modelname = Info_ValueForKey (userinfo, "model");
02496         SetupGameGhoul2Model(ent, modelname, NULL);
02497 
02498         if (ent->ghoul2 && ent->client)
02499         {
02500                 ent->client->renderInfo.lastG2 = NULL; //update the renderinfo bolts next update.
02501         }
02502 
02503         if (g_gametype.integer == GT_POWERDUEL && client->sess.sessionTeam != TEAM_SPECTATOR &&
02504                 client->sess.duelTeam == DUELTEAM_FREE)
02505         {
02506                 SetTeam(ent, "s");
02507         }
02508         else
02509         {
02510                 if (g_gametype.integer == GT_SIEGE && (!gSiegeRoundBegun || gSiegeRoundEnded))
02511                 {
02512                         SetTeamQuick(ent, TEAM_SPECTATOR, qfalse);
02513                 }
02514         
02515                 if ((ent->r.svFlags & SVF_BOT) &&
02516                         g_gametype.integer != GT_SIEGE)
02517                 {
02518                         char *saberVal = Info_ValueForKey(userinfo, "saber1");
02519                         char *saber2Val = Info_ValueForKey(userinfo, "saber2");
02520 
02521                         if (!saberVal || !saberVal[0])
02522                         { //blah, set em up with a random saber
02523                                 int r = rand()%50;
02524                                 char sab1[1024];
02525                                 char sab2[1024];
02526 
02527                                 if (r <= 17)
02528                                 {
02529                                         strcpy(sab1, "Katarn");
02530                                         strcpy(sab2, "none");
02531                                 }
02532                                 else if (r <= 34)
02533                                 {
02534                                         strcpy(sab1, "Katarn");
02535                                         strcpy(sab2, "Katarn");
02536                                 }
02537                                 else
02538                                 {
02539                                         strcpy(sab1, "dual_1");
02540                                         strcpy(sab2, "none");
02541                                 }
02542                                 G_SetSaber(ent, 0, sab1, qfalse);
02543                                 G_SetSaber(ent, 0, sab2, qfalse);
02544                                 Info_SetValueForKey( userinfo, "saber1", sab1 );
02545                                 Info_SetValueForKey( userinfo, "saber2", sab2 );
02546                                 trap_SetUserinfo( clientNum, userinfo );
02547                         }
02548                         else
02549                         {
02550                                 G_SetSaber(ent, 0, saberVal, qfalse);
02551                         }
02552 
02553                         if (saberVal && saberVal[0] &&
02554                                 (!saber2Val || !saber2Val[0]))
02555                         {
02556                                 G_SetSaber(ent, 0, "none", qfalse);
02557                                 Info_SetValueForKey( userinfo, "saber2", "none" );
02558                                 trap_SetUserinfo( clientNum, userinfo );
02559                         }
02560                         else
02561                         {
02562                                 G_SetSaber(ent, 0, saber2Val, qfalse);
02563                         }
02564                 }
02565 
02566                 // locate ent at a spawn point
02567                 ClientSpawn( ent );
02568         }
02569 
02570         if ( client->sess.sessionTeam != TEAM_SPECTATOR ) {
02571                 // send event
02572                 tent = G_TempEntity( ent->client->ps.origin, EV_PLAYER_TELEPORT_IN );
02573                 tent->s.clientNum = ent->s.clientNum;
02574 
02575                 if ( g_gametype.integer != GT_DUEL || g_gametype.integer == GT_POWERDUEL ) {
02576                         trap_SendServerCommand( -1, va("print \"%s" S_COLOR_WHITE " %s\n\"", client->pers.netname, G_GetStringEdString("MP_SVGAME", "PLENTER")) );
02577                 }
02578         }
02579         G_LogPrintf( "ClientBegin: %i\n", clientNum );
02580 
02581         // count current clients and rank for scoreboard
02582         CalculateRanks();
02583 
02584         G_ClearClientLog(clientNum);
02585 }
02586 
02587 static qboolean AllForceDisabled(int force)
02588 {
02589         int i;
02590 
02591         if (force)
02592         {
02593                 for (i=0;i<NUM_FORCE_POWERS;i++)
02594                 {
02595                         if (!(force & (1<<i)))
02596                         {
02597                                 return qfalse;
02598                         }
02599                 }
02600 
02601                 return qtrue;
02602         }
02603 
02604         return qfalse;
02605 }
02606 
02607 //Convenient interface to set all my limb breakage stuff up -rww
02608 void G_BreakArm(gentity_t *ent, int arm)
02609 {
02610         int anim = -1;
02611 
02612         assert(ent && ent->client);
02613 
02614         if (ent->s.NPC_class == CLASS_VEHICLE || ent->localAnimIndex > 1)
02615         { //no broken limbs for vehicles and non-humanoids
02616                 return;
02617         }
02618 
02619         if (!arm)
02620         { //repair him
02621                 ent->client->ps.brokenLimbs = 0;
02622                 return;
02623         }
02624 
02625         if (ent->client->ps.fd.saberAnimLevel == SS_STAFF)
02626         { //I'm too lazy to deal with this as well for now.
02627                 return;
02628         }
02629 
02630         if (arm == BROKENLIMB_LARM)
02631         {
02632                 if (ent->client->saber[1].model[0] &&
02633                         ent->client->ps.weapon == WP_SABER &&
02634                         !ent->client->ps.saberHolstered &&
02635                         ent->client->saber[1].soundOff)
02636                 { //the left arm shuts off its saber upon being broken
02637                         G_Sound(ent, CHAN_AUTO, ent->client->saber[1].soundOff);
02638                 }
02639         }
02640 
02641         ent->client->ps.brokenLimbs = 0; //make sure it's cleared out
02642         ent->client->ps.brokenLimbs |= (1 << arm); //this arm is now marked as broken
02643 
02644         //Do a pain anim based on the side. Since getting your arm broken does tend to hurt.
02645         if (arm == BROKENLIMB_LARM)
02646         {
02647                 anim = BOTH_PAIN2;
02648         }
02649         else if (arm == BROKENLIMB_RARM)
02650         {
02651                 anim = BOTH_PAIN3;
02652         }
02653 
02654         if (anim == -1)
02655         {
02656                 return;
02657         }
02658 
02659         G_SetAnim(ent, &ent->client->pers.cmd, SETANIM_BOTH, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 0);
02660 
02661         //This could be combined into a single event. But I guess limbs don't break often enough to
02662         //worry about it.
02663         G_EntitySound( ent, CHAN_VOICE, G_SoundIndex("*pain25.wav") );
02664         //FIXME: A nice bone snapping sound instead if possible
02665         G_Sound(ent, CHAN_AUTO, G_SoundIndex( va("sound/player/bodyfall_human%i.wav", Q_irand(1, 3)) ));
02666 }
02667 
02668 //Update the ghoul2 instance anims based on the playerstate values
02669 #include "../namespace_begin.h"
02670 qboolean BG_SaberStanceAnim( int anim );
02671 qboolean PM_RunningAnim( int anim );
02672 #include "../namespace_end.h"
02673 void G_UpdateClientAnims(gentity_t *self, float animSpeedScale)
02674 {
02675         static int f;
02676         static int torsoAnim;
02677         static int legsAnim;
02678         static int firstFrame, lastFrame;
02679         static int aFlags;
02680         static float animSpeed, lAnimSpeedScale;
02681         qboolean setTorso = qfalse;
02682 
02683         torsoAnim = (self->client->ps.torsoAnim);
02684         legsAnim = (self->client->ps.legsAnim);
02685 
02686         if (self->client->ps.saberLockFrame)
02687         {
02688                 trap_G2API_SetBoneAnim(self->ghoul2, 0, "model_root", self->client->ps.saberLockFrame, self->client->ps.saberLockFrame+1, BONE_ANIM_OVERRIDE_FREEZE|BONE_ANIM_BLEND, animSpeedScale, level.time, -1, 150);
02689                 trap_G2API_SetBoneAnim(self->ghoul2, 0, "lower_lumbar", self->client->ps.saberLockFrame, self->client->ps.saberLockFrame+1, BONE_ANIM_OVERRIDE_FREEZE|BONE_ANIM_BLEND, animSpeedScale, level.time, -1, 150);
02690                 trap_G2API_SetBoneAnim(self->ghoul2, 0, "Motion", self->client->ps.saberLockFrame, self->client->ps.saberLockFrame+1, BONE_ANIM_OVERRIDE_FREEZE|BONE_ANIM_BLEND, animSpeedScale, level.time, -1, 150);
02691                 return;
02692         }
02693 
02694         if (self->localAnimIndex > 1 &&
02695                 bgAllAnims[self->localAnimIndex].anims[legsAnim].firstFrame == 0 &&
02696                 bgAllAnims[self->localAnimIndex].anims[legsAnim].numFrames == 0)
02697         { //We'll allow this for non-humanoids.
02698                 goto tryTorso;
02699         }
02700 
02701         if (self->client->legsAnimExecute != legsAnim || self->client->legsLastFlip != self->client->ps.legsFlip)
02702         {
02703                 animSpeed = 50.0f / bgAllAnims[self->localAnimIndex].anims[legsAnim].frameLerp;
02704                 lAnimSpeedScale = (animSpeed *= animSpeedScale);
02705 
02706                 if (bgAllAnims[self->localAnimIndex].anims[legsAnim].loopFrames != -1)
02707                 {
02708                         aFlags = BONE_ANIM_OVERRIDE_LOOP;
02709                 }
02710                 else
02711                 {
02712                         aFlags = BONE_ANIM_OVERRIDE_FREEZE;
02713                 }
02714 
02715                 if (animSpeed < 0)
02716                 {
02717                         lastFrame = bgAllAnims[self->localAnimIndex].anims[legsAnim].firstFrame;
02718                         firstFrame = bgAllAnims[self->localAnimIndex].anims[legsAnim].firstFrame + bgAllAnims[self->localAnimIndex].anims[legsAnim].numFrames;
02719                 }
02720                 else
02721                 {
02722                         firstFrame = bgAllAnims[self->localAnimIndex].anims[legsAnim].firstFrame;
02723                         lastFrame = bgAllAnims[self->localAnimIndex].anims[legsAnim].firstFrame + bgAllAnims[self->localAnimIndex].anims[legsAnim].numFrames;
02724                 }
02725 
02726                 aFlags |= BONE_ANIM_BLEND; //since client defaults to blend. Not sure if this will make much difference if any on server position, but it's here just for the sake of matching them.
02727 
02728                 trap_G2API_SetBoneAnim(self->ghoul2, 0, "model_root", firstFrame, lastFrame, aFlags, lAnimSpeedScale, level.time, -1, 150);
02729                 self->client->legsAnimExecute = legsAnim;
02730                 self->client->legsLastFlip = self->client->ps.legsFlip;
02731         }
02732 
02733 tryTorso:
02734         if (self->localAnimIndex > 1 &&
02735                 bgAllAnims[self->localAnimIndex].anims[torsoAnim].firstFrame == 0 &&
02736                 bgAllAnims[self->localAnimIndex].anims[torsoAnim].numFrames == 0)
02737 
02738         { //If this fails as well just return.
02739                 return;
02740         }
02741         else if (self->s.number >= MAX_CLIENTS &&
02742                 self->s.NPC_class == CLASS_VEHICLE)
02743         { //we only want to set the root bone for vehicles
02744                 return;
02745         }
02746 
02747         if ((self->client->torsoAnimExecute != torsoAnim || self->client->torsoLastFlip != self->client->ps.torsoFlip) &&
02748                 !self->noLumbar)
02749         {
02750                 aFlags = 0;
02751                 animSpeed = 0;
02752 
02753                 f = torsoAnim;
02754 
02755                 BG_SaberStartTransAnim(self->s.number, self->client->ps.fd.saberAnimLevel, self->client->ps.weapon, f, &animSpeedScale, self->client->ps.brokenLimbs);
02756 
02757                 animSpeed = 50.0f / bgAllAnims[self->localAnimIndex].anims[f].frameLerp;
02758                 lAnimSpeedScale = (animSpeed *= animSpeedScale);
02759 
02760                 if (bgAllAnims[self->localAnimIndex].anims[f].loopFrames != -1)
02761                 {
02762                         aFlags = BONE_ANIM_OVERRIDE_LOOP;
02763                 }
02764                 else
02765                 {
02766                         aFlags = BONE_ANIM_OVERRIDE_FREEZE;
02767                 }
02768 
02769                 aFlags |= BONE_ANIM_BLEND; //since client defaults to blend. Not sure if this will make much difference if any on client position, but it's here just for the sake of matching them.
02770 
02771                 if (animSpeed < 0)
02772                 {
02773                         lastFrame = bgAllAnims[self->localAnimIndex].anims[f].firstFrame;
02774                         firstFrame = bgAllAnims[self->localAnimIndex].anims[f].firstFrame + bgAllAnims[self->localAnimIndex].anims[f].numFrames;
02775                 }
02776                 else
02777                 {
02778                         firstFrame = bgAllAnims[self->localAnimIndex].anims[f].firstFrame;
02779                         lastFrame = bgAllAnims[self->localAnimIndex].anims[f].firstFrame + bgAllAnims[self->localAnimIndex].anims[f].numFrames;
02780                 }
02781 
02782                 trap_G2API_SetBoneAnim(self->ghoul2, 0, "lower_lumbar", firstFrame, lastFrame, aFlags, lAnimSpeedScale, level.time, /*firstFrame why was it this before?*/-1, 150);
02783 
02784                 self->client->torsoAnimExecute = torsoAnim;
02785                 self->client->torsoLastFlip = self->client->ps.torsoFlip;
02786                 
02787                 setTorso = qtrue;
02788         }
02789 
02790         if (setTorso &&
02791                 self->localAnimIndex <= 1)
02792         { //only set the motion bone for humanoids.
02793                 trap_G2API_SetBoneAnim(self->ghoul2, 0, "Motion", firstFrame, lastFrame, aFlags, lAnimSpeedScale, level.time, -1, 150);
02794         }
02795 
02796 #if 0 //disabled for now
02797         if (self->client->ps.brokenLimbs != self->client->brokenLimbs ||
02798                 setTorso)
02799         {
02800                 if (self->localAnimIndex <= 1 && self->client->ps.brokenLimbs &&
02801                         (self->client->ps.brokenLimbs & (1 << BROKENLIMB_LARM)))
02802                 { //broken left arm
02803                         char *brokenBone = "lhumerus";
02804                         animation_t *armAnim;
02805                         int armFirstFrame;
02806                         int armLastFrame;
02807                         int armFlags = 0;
02808                         float armAnimSpeed;
02809 
02810                         armAnim = &bgAllAnims[self->localAnimIndex].anims[ BOTH_DEAD21 ];
02811                         self->client->brokenLimbs = self->client->ps.brokenLimbs;
02812 
02813                         armFirstFrame = armAnim->firstFrame;
02814                         armLastFrame = armAnim->firstFrame+armAnim->numFrames;
02815                         armAnimSpeed = 50.0f / armAnim->frameLerp;
02816                         armFlags = (BONE_ANIM_OVERRIDE_LOOP|BONE_ANIM_BLEND);
02817 
02818                         trap_G2API_SetBoneAnim(self->ghoul2, 0, brokenBone, armFirstFrame, armLastFrame, armFlags, armAnimSpeed, level.time, -1, 150);
02819                 }
02820                 else if (self->localAnimIndex <= 1 && self->client->ps.brokenLimbs &&
02821                         (self->client->ps.brokenLimbs & (1 << BROKENLIMB_RARM)))
02822                 { //broken right arm
02823                         char *brokenBone = "rhumerus";
02824                         char *supportBone = "lhumerus";
02825 
02826                         self->client->brokenLimbs = self->client->ps.brokenLimbs;
02827 
02828                         //Only put the arm in a broken pose if the anim is such that we
02829                         //want to allow it.
02830                         if ((//self->client->ps.weapon == WP_MELEE ||
02831                                 self->client->ps.weapon != WP_SABER ||
02832                                 BG_SaberStanceAnim(self->client->ps.torsoAnim) ||
02833                                 PM_RunningAnim(self->client->ps.torsoAnim)) &&
02834                                 (!self->client->saber[1].model[0] || self->client->ps.weapon != WP_SABER))
02835                         {
02836                                 int armFirstFrame;
02837                                 int armLastFrame;
02838                                 int armFlags = 0;
02839                                 float armAnimSpeed;
02840                                 animation_t *armAnim;
02841 
02842                                 if (self->client->ps.weapon == WP_MELEE ||
02843                                         self->client->ps.weapon == WP_SABER ||
02844                                         self->client->ps.weapon == WP_BRYAR_PISTOL)
02845                                 { //don't affect this arm if holding a gun, just make the other arm support it
02846                                         armAnim = &bgAllAnims[self->localAnimIndex].anims[ BOTH_ATTACK2 ];
02847 
02848                                         //armFirstFrame = armAnim->firstFrame;
02849                                         armFirstFrame = armAnim->firstFrame+armAnim->numFrames;
02850                                         armLastFrame = armAnim->firstFrame+armAnim->numFrames;
02851                                         armAnimSpeed = 50.0f / armAnim->frameLerp;
02852                                         armFlags = (BONE_ANIM_OVERRIDE_LOOP|BONE_ANIM_BLEND);
02853 
02854                                         trap_G2API_SetBoneAnim(self->ghoul2, 0, brokenBone, armFirstFrame, armLastFrame, armFlags, armAnimSpeed, level.time, -1, 150);
02855                                 }
02856                                 else
02857                                 { //we want to keep the broken bone updated for some cases
02858                                         trap_G2API_SetBoneAnim(self->ghoul2, 0, brokenBone, firstFrame, lastFrame, aFlags, lAnimSpeedScale, level.time, -1, 150);
02859                                 }
02860 
02861                                 if (self->client->ps.torsoAnim != BOTH_MELEE1 &&
02862                                         self->client->ps.torsoAnim != BOTH_MELEE2 &&
02863                                         (self->client->ps.torsoAnim == TORSO_WEAPONREADY2 || self->client->ps.torsoAnim == BOTH_ATTACK2 || self->client->ps.weapon < WP_BRYAR_PISTOL))
02864                                 {
02865                                         //Now set the left arm to "support" the right one
02866                                         armAnim = &bgAllAnims[self->localAnimIndex].anims[ BOTH_STAND2 ];
02867                                         armFirstFrame = armAnim->firstFrame;
02868                                         armLastFrame = armAnim->firstFrame+armAnim->numFrames;
02869                                         armAnimSpeed = 50.0f / armAnim->frameLerp;
02870                                         armFlags = (BONE_ANIM_OVERRIDE_LOOP|BONE_ANIM_BLEND);
02871 
02872                                         trap_G2API_SetBoneAnim(self->ghoul2, 0, supportBone, armFirstFrame, armLastFrame, armFlags, armAnimSpeed, level.time, -1, 150);
02873                                 }
02874                                 else
02875                                 { //we want to keep the support bone updated for some cases
02876                                         trap_G2API_SetBoneAnim(self->ghoul2, 0, supportBone, firstFrame, lastFrame, aFlags, lAnimSpeedScale, level.time, -1, 150);
02877                                 }
02878                         }
02879                         else
02880                         { //otherwise, keep it set to the same as the torso
02881                                 trap_G2API_SetBoneAnim(self->ghoul2, 0, brokenBone, firstFrame, lastFrame, aFlags, lAnimSpeedScale, level.time, -1, 150);
02882                                 trap_G2API_SetBoneAnim(self->ghoul2, 0, supportBone, firstFrame, lastFrame, aFlags, lAnimSpeedScale, level.time, -1, 150);
02883                         }
02884                 }
02885                 else if (self->client->brokenLimbs)
02886                 { //remove the bone now so it can be set again
02887                         char *brokenBone = NULL;
02888                         int broken = 0;
02889 
02890                         //Warning: Don't remove bones that you've added as bolts unless you want to invalidate your bolt index
02891                         //(well, in theory, I haven't actually run into the problem)
02892                         if (self->client->brokenLimbs & (1<<BROKENLIMB_LARM))
02893                         {
02894                                 brokenBone = "lhumerus";
02895                                 broken |= (1<<BROKENLIMB_LARM);
02896                         }
02897                         else if (self->client->brokenLimbs & (1<<BROKENLIMB_RARM))
02898                         { //can only have one arm broken at once.
02899                                 brokenBone = "rhumerus";
02900                                 broken |= (1<<BROKENLIMB_RARM);
02901 
02902                                 //want to remove the support bone too then
02903                                 trap_G2API_SetBoneAnim(self->ghoul2, 0, "lhumerus", 0, 1, 0, 0, level.time, -1, 0);
02904                                 trap_G2API_RemoveBone(self->ghoul2, "lhumerus", 0);
02905                         }
02906 
02907                         assert(brokenBone);
02908 
02909                         //Set the flags and stuff to 0, so that the remove will succeed
02910                         trap_G2API_SetBoneAnim(self->ghoul2, 0, brokenBone, 0, 1, 0, 0, level.time, -1, 0);
02911 
02912                         //Now remove it
02913                         trap_G2API_RemoveBone(self->ghoul2, brokenBone, 0);
02914                         self->client->brokenLimbs &= ~broken;
02915                 }
02916         }
02917 #endif
02918 }
02919 
02920 /*
02921 ===========
02922 ClientSpawn
02923 
02924 Called every time a client is placed fresh in the world:
02925 after the first ClientBegin, and after each respawn
02926 Initializes all non-persistant parts of playerState
02927 ============
02928 */
02929 extern qboolean WP_HasForcePowers( const playerState_t *ps );
02930 void ClientSpawn(gentity_t *ent) {
02931         int                                     index;
02932         vec3_t                          spawn_origin, spawn_angles;
02933         gclient_t                       *client;
02934         int                                     i;
02935         clientPersistant_t      saved;
02936         clientSession_t         savedSess;
02937         int                                     persistant[MAX_PERSISTANT];
02938         gentity_t                       *spawnPoint;
02939         int                                     flags, gameFlags;
02940         int                                     savedPing;
02941         int                                     accuracy_hits, accuracy_shots;
02942         int                                     eventSequence;
02943         char                            userinfo[MAX_INFO_STRING];
02944         forcedata_t                     savedForce;
02945         int                                     saveSaberNum = ENTITYNUM_NONE;
02946         int                                     wDisable = 0;
02947         int                                     savedSiegeIndex = 0;
02948         int                                     maxHealth;
02949         saberInfo_t                     saberSaved[MAX_SABERS];
02950         int                                     l = 0;
02951         void                            *g2WeaponPtrs[MAX_SABERS];
02952         char                            *value;
02953         char                            *saber;
02954         qboolean                        changedSaber = qfalse;
02955         qboolean                        inSiegeWithClass = qfalse;
02956 
02957         index = ent - g_entities;
02958         client = ent->client;
02959 
02960         //first we want the userinfo so we can see if we should update this client's saber -rww
02961         trap_GetUserinfo( index, userinfo, sizeof(userinfo) );
02962         while (l < MAX_SABERS)
02963         {
02964                 switch (l)
02965                 {
02966                 case 0:
02967                         saber = &ent->client->sess.saberType[0];
02968                         break;
02969                 case 1:
02970                         saber = &ent->client->sess.saber2Type[0];
02971                         break;
02972                 default:
02973                         saber = NULL;
02974                         break;
02975                 }
02976 
02977                 value = Info_ValueForKey (userinfo, va("saber%i", l+1));
02978                 if (saber &&
02979                         value &&
02980                         (Q_stricmp(value, saber) || !saber[0] || !ent->client->saber[0].model[0]))
02981                 { //doesn't match up (or our session saber is BS), we want to try setting it
02982                         if (G_SetSaber(ent, l, value, qfalse))
02983                         {
02984                                 changedSaber = qtrue;
02985                         }
02986                         else if (!saber[0] || !ent->client->saber[0].model[0])
02987                         { //Well, we still want to say they changed then (it means this is siege and we have some overrides)
02988                                 changedSaber = qtrue;
02989                         }
02990                 }
02991                 l++;
02992         }
02993 
02994         if (changedSaber)
02995         { //make sure our new info is sent out to all the other clients, and give us a valid stance
02996                 ClientUserinfoChanged( ent->s.number );
02997 
02998                 //make sure the saber models are updated
02999                 G_SaberModelSetup(ent);
03000 
03001                 l = 0;
03002                 while (l < MAX_SABERS)
03003                 { //go through and make sure both sabers match the userinfo
03004                         switch (l)
03005                         {
03006                         case 0:
03007                                 saber = &ent->client->sess.saberType[0];
03008                                 break;
03009                         case 1:
03010                                 saber = &ent->client->sess.saber2Type[0];
03011                                 break;
03012                         default:
03013                                 saber = NULL;
03014                                 break;
03015                         }
03016 
03017                         value = Info_ValueForKey (userinfo, va("saber%i", l+1));
03018 
03019                         if (Q_stricmp(value, saber))
03020                         { //they don't match up, force the user info
03021                                 Info_SetValueForKey(userinfo, va("saber%i", l+1), saber);
03022                                 trap_SetUserinfo( ent->s.number, userinfo );
03023                         }
03024                         l++;
03025                 }
03026 
03027                 if (ent->client->saber[0].model[0] &&
03028                         ent->client->saber[1].model[0])
03029                 { //dual
03030                         ent->client->ps.fd.saberAnimLevelBase = ent->client->ps.fd.saberAnimLevel = ent->client->ps.fd.saberDrawAnimLevel = SS_DUAL;
03031                 }
03032                 else if ((ent->client->saber[0].saberFlags&SFL_TWO_HANDED))
03033                 { //staff
03034                         ent->client->ps.fd.saberAnimLevel = ent->client->ps.fd.saberDrawAnimLevel = SS_STAFF;
03035                 }
03036                 else
03037                 {
03038                         if (ent->client->sess.saberLevel < SS_FAST)
03039                         {
03040                                 ent->client->sess.saberLevel = SS_FAST;
03041                         }
03042                         else if (ent->client->sess.saberLevel > SS_STRONG)
03043                         {
03044                                 ent->client->sess.saberLevel = SS_STRONG;
03045                         }
03046                         ent->client->ps.fd.saberAnimLevelBase = ent->client->ps.fd.saberAnimLevel = ent->client->ps.fd.saberDrawAnimLevel = ent->client->sess.saberLevel;
03047 
03048                         if (g_gametype.integer != GT_SIEGE &&
03049                                 ent->client->ps.fd.saberAnimLevel > ent->client->ps.fd.forcePowerLevel[FP_SABER_OFFENSE])
03050                         {
03051                                 ent->client->ps.fd.saberAnimLevelBase = ent->client->ps.fd.saberAnimLevel = ent->client->ps.fd.saberDrawAnimLevel = ent->client->sess.saberLevel = ent->client->ps.fd.forcePowerLevel[FP_SABER_OFFENSE];
03052                         }
03053                 }
03054                 if ( g_gametype.integer != GT_SIEGE )
03055                 {
03056                         //let's just make sure the styles we chose are cool
03057                         if ( !WP_SaberStyleValidForSaber( &ent->client->saber[0], &ent->client->saber[1], ent->client->ps.saberHolstered, ent->client->ps.fd.saberAnimLevel ) )
03058                         {
03059                                 WP_UseFirstValidSaberStyle( &ent->client->saber[0], &ent->client->saber[1], ent->client->ps.saberHolstered, &ent->client->ps.fd.saberAnimLevel );
03060                                 ent->client->ps.fd.saberAnimLevelBase = ent->client->saberCycleQueue = ent->client->ps.fd.saberAnimLevel;
03061                         }
03062                 }
03063         }
03064         l = 0;
03065 
03066         if (client->ps.fd.forceDoInit)
03067         { //force a reread of force powers
03068                 WP_InitForcePowers( ent );
03069                 client->ps.fd.forceDoInit = 0;
03070         }
03071 
03072         if (ent->client->ps.fd.saberAnimLevel != SS_STAFF &&
03073                 ent->client->ps.fd.saberAnimLevel != SS_DUAL &&
03074                 ent->client->ps.fd.saberAnimLevel == ent->client->ps.fd.saberDrawAnimLevel &&
03075                 ent->client->ps.fd.saberAnimLevel == ent->client->sess.saberLevel)
03076         {
03077                 if (ent->client->sess.saberLevel < SS_FAST)
03078                 {
03079                         ent->client->sess.saberLevel = SS_FAST;
03080                 }
03081                 else if (ent->client->sess.saberLevel > SS_STRONG)
03082                 {
03083                         ent->client->sess.saberLevel = SS_STRONG;
03084                 }
03085                 ent->client->ps.fd.saberAnimLevel = ent->client->ps.fd.saberDrawAnimLevel = ent->client->sess.saberLevel;
03086 
03087                 if (g_gametype.integer != GT_SIEGE &&
03088                         ent->client->ps.fd.saberAnimLevel > ent->client->ps.fd.forcePowerLevel[FP_SABER_OFFENSE])
03089                 {
03090                         ent->client->ps.fd.saberAnimLevel = ent->client->ps.fd.saberDrawAnimLevel = ent->client->sess.saberLevel = ent->client->ps.fd.forcePowerLevel[FP_SABER_OFFENSE];
03091                 }
03092         }
03093 
03094         // find a spawn point
03095         // do it before setting health back up, so farthest
03096         // ranging doesn't count this client
03097         if ( client->sess.sessionTeam == TEAM_SPECTATOR ) {
03098                 spawnPoint = SelectSpectatorSpawnPoint ( 
03099                                                 spawn_origin, spawn_angles);
03100         } else if (g_gametype.integer == GT_CTF || g_gametype.integer == GT_CTY) {
03101                 // all base oriented team games use the CTF spawn points
03102                 spawnPoint = SelectCTFSpawnPoint ( 
03103                                                 client->sess.sessionTeam, 
03104                                                 client->pers.teamState.state, 
03105                                                 spawn_origin, spawn_angles);
03106         }
03107         else if (g_gametype.integer == GT_SIEGE)
03108         {
03109                 spawnPoint = SelectSiegeSpawnPoint (
03110                                                 client->siegeClass,
03111                                                 client->sess.sessionTeam, 
03112                                                 client->pers.teamState.state, 
03113                                                 spawn_origin, spawn_angles);
03114         }
03115         else {
03116                 do {
03117                         if (g_gametype.integer == GT_POWERDUEL)
03118                         {
03119                                 spawnPoint = SelectDuelSpawnPoint(client->sess.duelTeam, client->ps.origin, spawn_origin, spawn_angles);
03120                         }
03121                         else if (g_gametype.integer == GT_DUEL)
03122                         {       // duel 
03123                                 spawnPoint = SelectDuelSpawnPoint(DUELTEAM_SINGLE, client->ps.origin, spawn_origin, spawn_angles);
03124                         }
03125                         else
03126                         {
03127                                 // the first spawn should be at a good looking spot
03128                                 if ( !client->pers.initialSpawn && client->pers.localClient ) {
03129                                         client->pers.initialSpawn = qtrue;
03130                                         spawnPoint = SelectInitialSpawnPoint( spawn_origin, spawn_angles, client->sess.sessionTeam );
03131                                 } else {
03132                                         // don't spawn near existing origin if possible
03133                                         spawnPoint = SelectSpawnPoint ( 
03134                                                 client->ps.origin, 
03135                                                 spawn_origin, spawn_angles, client->sess.sessionTeam );
03136                                 }
03137                         }
03138 
03139                         // Tim needs to prevent bots from spawning at the initial point
03140                         // on q3dm0...
03141                         if ( ( spawnPoint->flags & FL_NO_BOTS ) && ( ent->r.svFlags & SVF_BOT ) ) {
03142                                 continue;       // try again
03143                         }
03144                         // just to be symetric, we have a nohumans option...
03145                         if ( ( spawnPoint->flags & FL_NO_HUMANS ) && !( ent->r.svFlags & SVF_BOT ) ) {
03146                                 continue;       // try again
03147                         }
03148 
03149                         break;
03150 
03151                 } while ( 1 );
03152         }
03153         client->pers.teamState.state = TEAM_ACTIVE;
03154 
03155         // toggle the teleport bit so the client knows to not lerp
03156         // and never clear the voted flag
03157         flags = ent->client->ps.eFlags & (EF_TELEPORT_BIT );
03158         flags ^= EF_TELEPORT_BIT;
03159         gameFlags = ent->client->mGameFlags & ( PSG_VOTED | PSG_TEAMVOTED);
03160 
03161         // clear everything but the persistant data
03162 
03163         saved = client->pers;
03164         savedSess = client->sess;
03165         savedPing = client->ps.ping;
03166 //      savedAreaBits = client->areabits;
03167         accuracy_hits = client->accuracy_hits;
03168         accuracy_shots = client->accuracy_shots;
03169         for ( i = 0 ; i < MAX_PERSISTANT ; i++ ) {
03170                 persistant[i] = client->ps.persistant[i];
03171         }
03172         eventSequence = client->ps.eventSequence;
03173 
03174         savedForce = client->ps.fd;
03175 
03176         saveSaberNum = client->ps.saberEntityNum;
03177 
03178         savedSiegeIndex = client->siegeClass;
03179 
03180         l = 0;
03181         while (l < MAX_SABERS)
03182         {
03183                 saberSaved[l] = client->saber[l];
03184                 g2WeaponPtrs[l] = client->weaponGhoul2[l];
03185                 l++;
03186         }
03187 
03188         i = 0;
03189         while (i < HL_MAX)
03190         {
03191                 ent->locationDamage[i] = 0;
03192                 i++;
03193         }
03194 
03195         memset (client, 0, sizeof(*client)); // bk FIXME: Com_Memset?
03196         client->bodyGrabIndex = ENTITYNUM_NONE;
03197 
03198         //Get the skin RGB based on his userinfo
03199         value = Info_ValueForKey (userinfo, "char_color_red");
03200         if (value)
03201         {
03202                 client->ps.customRGBA[0] = atoi(value);
03203         }
03204         else
03205         {
03206                 client->ps.customRGBA[0] = 255;
03207         }
03208 
03209         value = Info_ValueForKey (userinfo, "char_color_green");
03210         if (value)
03211         {
03212                 client->ps.customRGBA[1] = atoi(value);
03213         }
03214         else
03215         {
03216                 client->ps.customRGBA[1] = 255;
03217         }
03218 
03219         value = Info_ValueForKey (userinfo, "char_color_blue");
03220         if (value)
03221         {
03222                 client->ps.customRGBA[2] = atoi(value);
03223         }
03224         else
03225         {
03226                 client->ps.customRGBA[2] = 255;
03227         }
03228 
03229         if ((client->ps.customRGBA[0]+client->ps.customRGBA[1]+client->ps.customRGBA[2]) < 100)
03230         { //hmm, too dark!
03231                 client->ps.customRGBA[0] = client->ps.customRGBA[1] = client->ps.customRGBA[2] = 255;
03232         }
03233 
03234         client->ps.customRGBA[3]=255;
03235 
03236         client->siegeClass = savedSiegeIndex;
03237 
03238         l = 0;
03239         while (l < MAX_SABERS)
03240         {
03241                 client->saber[l] = saberSaved[l];
03242                 client->weaponGhoul2[l] = g2WeaponPtrs[l];
03243                 l++;
03244         }
03245 
03246         //or the saber ent num
03247         client->ps.saberEntityNum = saveSaberNum;
03248         client->saberStoredIndex = saveSaberNum;
03249 
03250         client->ps.fd = savedForce;
03251 
03252         client->ps.duelIndex = ENTITYNUM_NONE;
03253 
03254         //spawn with 100
03255         client->ps.jetpackFuel = 100;
03256         client->ps.cloakFuel = 100;
03257 
03258         client->pers = saved;
03259         client->sess = savedSess;
03260         client->ps.ping = savedPing;
03261 //      client->areabits = savedAreaBits;
03262         client->accuracy_hits = accuracy_hits;
03263         client->accuracy_shots = accuracy_shots;
03264         client->lastkilled_client = -1;
03265 
03266         for ( i = 0 ; i < MAX_PERSISTANT ; i++ ) {
03267                 client->ps.persistant[i] = persistant[i];
03268         }
03269         client->ps.eventSequence = eventSequence;
03270         // increment the spawncount so the client will detect the respawn
03271         client->ps.persistant[PERS_SPAWN_COUNT]++;
03272         client->ps.persistant[PERS_TEAM] = client->sess.sessionTeam;
03273 
03274         client->airOutTime = level.time + 12000;
03275 
03276         // set max health
03277         if (g_gametype.integer == GT_SIEGE && client->siegeClass != -1)
03278         {
03279                 siegeClass_t *scl = &bgSiegeClasses[client->siegeClass];
03280                 maxHealth = 100;
03281 
03282                 if (scl->maxhealth)
03283                 {
03284                         maxHealth = scl->maxhealth;
03285                 }
03286         }
03287         else
03288         {
03289                 maxHealth = 100;
03290         }
03291         client->pers.maxHealth = maxHealth;//atoi( Info_ValueForKey( userinfo, "handicap" ) );
03292         if ( client->pers.maxHealth < 1 || client->pers.maxHealth > maxHealth ) {
03293                 client->pers.maxHealth = 100;
03294         }
03295         // clear entity values
03296         client->ps.stats[STAT_MAX_HEALTH] = client->pers.maxHealth;
03297         client->ps.eFlags = flags;
03298         client->mGameFlags = gameFlags;
03299 
03300         ent->s.groundEntityNum = ENTITYNUM_NONE;
03301         ent->client = &level.clients[index];
03302         ent->playerState = &ent->client->ps;
03303         ent->takedamage = qtrue;
03304         ent->inuse = qtrue;
03305         ent->classname = "player";
03306         ent->r.contents = CONTENTS_BODY;
03307         ent->clipmask = MASK_PLAYERSOLID;
03308         ent->die = player_die;
03309         ent->waterlevel = 0;
03310         ent->watertype = 0;
03311         ent->flags = 0;
03312         
03313         VectorCopy (playerMins, ent->r.mins);
03314         VectorCopy (playerMaxs, ent->r.maxs);
03315         client->ps.crouchheight = CROUCH_MAXS_2;
03316         client->ps.standheight = DEFAULT_MAXS_2;
03317 
03318         client->ps.clientNum = index;
03319         //give default weapons
03320         client->ps.stats[STAT_WEAPONS] = ( 1 << WP_NONE );
03321 
03322         if (g_gametype.integer == GT_DUEL || g_gametype.integer == GT_POWERDUEL)
03323         {
03324                 wDisable = g_duelWeaponDisable.integer;
03325         }
03326         else
03327         {
03328                 wDisable = g_weaponDisable.integer;
03329         }
03330 
03331 
03332 
03333         if ( g_gametype.integer != GT_HOLOCRON 
03334                 && g_gametype.integer != GT_JEDIMASTER 
03335                 && !HasSetSaberOnly()
03336                 && !AllForceDisabled( g_forcePowerDisable.integer )
03337                 && g_trueJedi.integer )
03338         {
03339                 if ( g_gametype.integer >= GT_TEAM && (client->sess.sessionTeam == TEAM_BLUE || client->sess.sessionTeam == TEAM_RED) )
03340                 {//In Team games, force one side to be merc and other to be jedi
03341                         if ( level.numPlayingClients > 0 )
03342                         {//already someone in the game
03343                                 int             i, forceTeam = TEAM_SPECTATOR;
03344                                 for ( i = 0 ; i < level.maxclients ; i++ ) 
03345                                 {
03346                                         if ( level.clients[i].pers.connected == CON_DISCONNECTED ) {
03347                                                 continue;
03348                                         }
03349                                         if ( level.clients[i].sess.sessionTeam == TEAM_BLUE || level.clients[i].sess.sessionTeam == TEAM_RED ) 
03350                                         {//in-game
03351                                                 if ( WP_HasForcePowers( &level.clients[i].ps ) )
03352                                                 {//this side is using force
03353                                                         forceTeam = level.clients[i].sess.sessionTeam;
03354                                                 }
03355                                                 else
03356                                                 {//other team is using force
03357                                                         if ( level.clients[i].sess.sessionTeam == TEAM_BLUE )
03358                                                         {
03359                                                                 forceTeam = TEAM_RED;
03360                                                         }
03361                                                         else
03362                                                         {
03363                                                                 forceTeam = TEAM_BLUE;
03364                                                         }
03365                                                 }
03366                                                 break;
03367                                         }
03368                                 }
03369                                 if ( WP_HasForcePowers( &client->ps ) && client->sess.sessionTeam != forceTeam )
03370                                 {//using force but not on right team, switch him over
03371                                         const char *teamName = TeamName( forceTeam );
03372                                         //client->sess.sessionTeam = forceTeam;
03373                                         SetTeam( ent, (char *)teamName );
03374                                         return;
03375                                 }
03376                         }
03377                 }
03378 
03379                 if ( WP_HasForcePowers( &client->ps ) )
03380                 {
03381                         client->ps.trueNonJedi = qfalse;
03382                         client->ps.trueJedi = qtrue;
03383                         //make sure they only use the saber
03384                         client->ps.weapon = WP_SABER;
03385                         client->ps.stats[STAT_WEAPONS] = (1 << WP_SABER);
03386                 }
03387                 else
03388                 {//no force powers set
03389                         client->ps.trueNonJedi = qtrue;
03390                         client->ps.trueJedi = qfalse;
03391                         if (!wDisable || !(wDisable & (1 << WP_BRYAR_PISTOL)))
03392                         {
03393                                 client->ps.stats[STAT_WEAPONS] |= ( 1 << WP_BRYAR_PISTOL );
03394                         }
03395                         if (!wDisable || !(wDisable & (1 << WP_BLASTER)))
03396                         {
03397                                 client->ps.stats[STAT_WEAPONS] |= ( 1 << WP_BLASTER );
03398                         }
03399                         if (!wDisable || !(wDisable & (1 << WP_BOWCASTER)))
03400                         {
03401                                 client->ps.stats[STAT_WEAPONS] |= ( 1 << WP_BOWCASTER );
03402                         }
03403                         client->ps.stats[STAT_WEAPONS] &= ~(1 << WP_SABER);
03404                         client->ps.stats[STAT_WEAPONS] |= (1 << WP_MELEE);
03405                         client->ps.ammo[AMMO_POWERCELL] = ammoData[AMMO_POWERCELL].max;
03406                         client->ps.weapon = WP_BRYAR_PISTOL;
03407                 }
03408         }
03409         else
03410         {//jediVmerc is incompatible with this gametype, turn it off!
03411                 trap_Cvar_Set( "g_jediVmerc", "0" );
03412                 if (g_gametype.integer == GT_HOLOCRON)
03413                 {
03414                         //always get free saber level 1 in holocron
03415                         client->ps.stats[STAT_WEAPONS] |= ( 1 << WP_SABER );    //these are precached in g_items, ClearRegisteredItems()
03416                 }
03417                 else
03418                 {
03419                         if (client->ps.fd.forcePowerLevel[FP_SABER_OFFENSE])
03420                         {
03421                                 client->ps.stats[STAT_WEAPONS] |= ( 1 << WP_SABER );    //these are precached in g_items, ClearRegisteredItems()
03422                         }
03423                         else
03424                         { //if you don't have saber attack rank then you don't get a saber
03425                                 client->ps.stats[STAT_WEAPONS] |= (1 << WP_MELEE);
03426                         }
03427                 }
03428 
03429                 if (g_gametype.integer != GT_SIEGE)
03430                 {
03431                         if (!wDisable || !(wDisable & (1 << WP_BRYAR_PISTOL)))
03432                         {
03433                                 client->ps.stats[STAT_WEAPONS] |= ( 1 << WP_BRYAR_PISTOL );
03434                         }
03435                         else if (g_gametype.integer == GT_JEDIMASTER)
03436                         {
03437                                 client->ps.stats[STAT_WEAPONS] |= ( 1 << WP_BRYAR_PISTOL );
03438                         }
03439                 }
03440 
03441                 if (g_gametype.integer == GT_JEDIMASTER)
03442                 {
03443                         client->ps.stats[STAT_WEAPONS] &= ~(1 << WP_SABER);
03444                         client->ps.stats[STAT_WEAPONS] |= (1 << WP_MELEE);
03445                 }
03446 
03447                 if (client->ps.stats[STAT_WEAPONS] & (1 << WP_SABER))
03448                 {
03449                         client->ps.weapon = WP_SABER;
03450                 }
03451                 else if (client->ps.stats[STAT_WEAPONS] & (1 << WP_BRYAR_PISTOL))
03452                 {
03453                         client->ps.weapon = WP_BRYAR_PISTOL;
03454                 }
03455                 else
03456                 {
03457                         client->ps.weapon = WP_MELEE;
03458                 }
03459         }
03460 
03461         /*
03462         client->ps.stats[STAT_HOLDABLE_ITEMS] |= ( 1 << HI_BINOCULARS );
03463         client->ps.stats[STAT_HOLDABLE_ITEM] = BG_GetItemIndexByTag(HI_BINOCULARS, IT_HOLDABLE);
03464         */
03465 
03466         if (g_gametype.integer == GT_SIEGE && client->siegeClass != -1 &&
03467                 client->sess.sessionTeam != TEAM_SPECTATOR)
03468         { //well then, we will use a custom weaponset for our class
03469                 int m = 0;
03470 
03471                 client->ps.stats[STAT_WEAPONS] = bgSiegeClasses[client->siegeClass].weapons;
03472 
03473                 if (client->ps.stats[STAT_WEAPONS] & (1 << WP_SABER))
03474                 {
03475                         client->ps.weapon = WP_SABER;
03476                 }
03477                 else if (client->ps.stats[STAT_WEAPONS] & (1 << WP_BRYAR_PISTOL))
03478                 {
03479                         client->ps.weapon = WP_BRYAR_PISTOL;
03480                 }
03481                 else
03482                 {
03483                         client->ps.weapon = WP_MELEE;
03484                 }
03485                 inSiegeWithClass = qtrue;
03486 
03487                 while (m < WP_NUM_WEAPONS)
03488                 {
03489                         if (client->ps.stats[STAT_WEAPONS] & (1 << m))
03490                         {
03491                                 if (client->ps.weapon != WP_SABER)
03492                                 { //try to find the highest ranking weapon we have
03493                                         if (m > client->ps.weapon)
03494                                         {
03495                                                 client->ps.weapon = m;
03496                                         }
03497                                 }
03498 
03499                                 if (m >= WP_BRYAR_PISTOL)
03500                                 { //Max his ammo out for all the weapons he has.
03501                                         if ( g_gametype.integer == GT_SIEGE 
03502                                                 && m == WP_ROCKET_LAUNCHER )
03503                                         {//don't give full ammo!
03504                                                 //FIXME: extern this and check it when getting ammo from supplier, pickups or ammo stations!
03505                                                 if ( client->siegeClass != -1 &&
03506                                                         (bgSiegeClasses[client->siegeClass].classflags & (1<<CFL_SINGLE_ROCKET)) )
03507                                                 {
03508                                                         client->ps.ammo[weaponData[m].ammoIndex] = 1;
03509                                                 }
03510                                                 else
03511                                                 {
03512                                                         client->ps.ammo[weaponData[m].ammoIndex] = 10;
03513                                                 }
03514                                         }
03515                                         else
03516                                         {
03517                                                 if ( g_gametype.integer == GT_SIEGE 
03518                                                         && client->siegeClass != -1
03519                                                         && (bgSiegeClasses[client->siegeClass].classflags & (1<<CFL_EXTRA_AMMO)) )
03520                                                 {//double ammo
03521                                                         client->ps.ammo[weaponData[m].ammoIndex] = ammoData[weaponData[m].ammoIndex].max*2;
03522                                                         client->ps.eFlags |= EF_DOUBLE_AMMO;
03523                                                 }
03524                                                 else
03525                                                 {
03526                                                         client->ps.ammo[weaponData[m].ammoIndex] = ammoData[weaponData[m].ammoIndex].max;
03527                                                 }
03528                                         }
03529                                 }
03530                         }
03531                         m++;
03532                 }
03533         }
03534 
03535         if (g_gametype.integer == GT_SIEGE &&
03536                 client->siegeClass != -1 &&
03537                 client->sess.sessionTeam != TEAM_SPECTATOR)
03538         { //use class-specified inventory
03539                 client->ps.stats[STAT_HOLDABLE_ITEMS] = bgSiegeClasses[client->siegeClass].invenItems;
03540                 client->ps.stats[STAT_HOLDABLE_ITEM] = 0;
03541         }
03542         else
03543         {
03544                 client->ps.stats[STAT_HOLDABLE_ITEMS] = 0;
03545                 client->ps.stats[STAT_HOLDABLE_ITEM] = 0;
03546         }
03547 
03548         if (g_gametype.integer == GT_SIEGE &&
03549                 client->siegeClass != -1 &&
03550                 bgSiegeClasses[client->siegeClass].powerups &&
03551                 client->sess.sessionTeam != TEAM_SPECTATOR)
03552         { //this class has some start powerups
03553                 i = 0;
03554                 while (i < PW_NUM_POWERUPS)
03555                 {
03556                         if (bgSiegeClasses[client->siegeClass].powerups & (1 << i))
03557                         {
03558                                 client->ps.powerups[i] = Q3_INFINITE;
03559                         }
03560                         i++;
03561                 }
03562         }
03563 
03564         if ( client->sess.sessionTeam == TEAM_SPECTATOR )
03565         {
03566                 client->ps.stats[STAT_WEAPONS] = 0;
03567                 client->ps.stats[STAT_HOLDABLE_ITEMS] = 0;
03568                 client->ps.stats[STAT_HOLDABLE_ITEM] = 0;
03569         }
03570 
03571 // nmckenzie: DESERT_SIEGE... or well, siege generally.  This was over-writing the max value, which was NOT good for siege.
03572         if ( inSiegeWithClass == qfalse )
03573         {
03574                 client->ps.ammo[AMMO_BLASTER] = 100; //ammoData[AMMO_BLASTER].max; //100 seems fair.
03575         }
03576 //      client->ps.ammo[AMMO_POWERCELL] = ammoData[AMMO_POWERCELL].max;
03577 //      client->ps.ammo[AMMO_FORCE] = ammoData[AMMO_FORCE].max;
03578 //      client->ps.ammo[AMMO_METAL_BOLTS] = ammoData[AMMO_METAL_BOLTS].max;
03579 //      client->ps.ammo[AMMO_ROCKETS] = ammoData[AMMO_ROCKETS].max;
03580 /*
03581         client->ps.stats[STAT_WEAPONS] = ( 1 << WP_BRYAR_PISTOL);
03582         if ( g_gametype.integer == GT_TEAM ) {
03583                 client->ps.ammo[WP_BRYAR_PISTOL] = 50;
03584         } else {
03585                 client->ps.ammo[WP_BRYAR_PISTOL] = 100;
03586         }
03587 */
03588         client->ps.rocketLockIndex = ENTITYNUM_NONE;
03589         client->ps.rocketLockTime = 0;
03590 
03591         //rww - Set here to initialize the circling seeker drone to off.
03592         //A quick note about this so I don't forget how it works again:
03593         //ps.genericEnemyIndex is kept in sync between the server and client.
03594         //When it gets set then an entitystate value of the same name gets
03595         //set along with an entitystate flag in the shared bg code. Which
03596         //is why a value needs to be both on the player state and entity state.
03597         //(it doesn't seem to just carry over the entitystate value automatically
03598         //because entity state value is derived from player state data or some
03599         //such)
03600         client->ps.genericEnemyIndex = -1;
03601 
03602         client->ps.isJediMaster = qfalse;
03603 
03604         if (client->ps.fallingToDeath)
03605         {
03606                 client->ps.fallingToDeath = 0;
03607                 client->noCorpse = qtrue;
03608         }
03609 
03610         //Do per-spawn force power initialization
03611         WP_SpawnInitForcePowers( ent );
03612 
03613         // health will count down towards max_health
03614         if (g_gametype.integer == GT_SIEGE &&
03615                 client->siegeClass != -1 &&
03616                 bgSiegeClasses[client->siegeClass].starthealth)
03617         { //class specifies a start health, so use it
03618                 ent->health = client->ps.stats[STAT_HEALTH] = bgSiegeClasses[client->siegeClass].starthealth;
03619         }
03620         else if ( g_gametype.integer == GT_DUEL || g_gametype.integer == GT_POWERDUEL )
03621         {//only start with 100 health in Duel
03622                 if ( g_gametype.integer == GT_POWERDUEL && client->sess.duelTeam == DUELTEAM_LONE )
03623                 {
03624                         if ( g_duel_fraglimit.integer )
03625                         {
03626                                 
03627                                 ent->health = client->ps.stats[STAT_HEALTH] = client->ps.stats[STAT_MAX_HEALTH] =
03628                                         g_powerDuelStartHealth.integer - ((g_powerDuelStartHealth.integer - g_powerDuelEndHealth.integer) * (float)client->sess.wins / (float)g_duel_fraglimit.integer);
03629                         }
03630                         else
03631                         {
03632                                 ent->health = client->ps.stats[STAT_HEALTH] = client->ps.stats[STAT_MAX_HEALTH] = 150;
03633                         }
03634                 }
03635                 else
03636                 {
03637                         ent->health = client->ps.stats[STAT_HEALTH] = client->ps.stats[STAT_MAX_HEALTH] = 100;
03638                 }
03639         }
03640         else if (client->ps.stats[STAT_MAX_HEALTH] <= 100)
03641         {
03642                 ent->health = client->ps.stats[STAT_HEALTH] = client->ps.stats[STAT_MAX_HEALTH] * 1.25;
03643         }
03644         else if (client->ps.stats[STAT_MAX_HEALTH] < 125)
03645         {
03646                 ent->health = client->ps.stats[STAT_HEALTH] = 125;
03647         }
03648         else
03649         {
03650                 ent->health = client->ps.stats[STAT_HEALTH] = client->ps.stats[STAT_MAX_HEALTH];
03651         }
03652 
03653         // Start with a small amount of armor as well.
03654         if (g_gametype.integer == GT_SIEGE &&
03655                 client->siegeClass != -1 /*&&
03656                 bgSiegeClasses[client->siegeClass].startarmor*/)
03657         { //class specifies a start armor amount, so use it
03658                 client->ps.stats[STAT_ARMOR] = bgSiegeClasses[client->siegeClass].startarmor;
03659         }
03660         else if ( g_gametype.integer == GT_DUEL || g_gametype.integer == GT_POWERDUEL )
03661         {//no armor in duel
03662                 client->ps.stats[STAT_ARMOR] = 0;
03663         }
03664         else
03665         {
03666                 client->ps.stats[STAT_ARMOR] = client->ps.stats[STAT_MAX_HEALTH] * 0.25;
03667         }
03668 
03669         G_SetOrigin( ent, spawn_origin );
03670         VectorCopy( spawn_origin, client->ps.origin );
03671 
03672         // the respawned flag will be cleared after the attack and jump keys come up
03673         client->ps.pm_flags |= PMF_RESPAWNED;
03674 
03675         trap_GetUsercmd( client - level.clients, &ent->client->pers.cmd );
03676         SetClientViewAngle( ent, spawn_angles );
03677 
03678         if ( ent->client->sess.sessionTeam == TEAM_SPECTATOR ) {
03679 
03680         } else {
03681                 G_KillBox( ent );
03682                 trap_LinkEntity (ent);
03683 
03684                 // force the base weapon up
03685                 //client->ps.weapon = WP_BRYAR_PISTOL;
03686                 //client->ps.weaponstate = FIRST_WEAPON;
03687                 if (client->ps.weapon <= WP_NONE)
03688                 {
03689                         client->ps.weapon = WP_BRYAR_PISTOL;
03690                 }
03691 
03692                 client->ps.torsoTimer = client->ps.legsTimer = 0;
03693 
03694                 if (client->ps.weapon == WP_SABER)
03695                 {
03696                         G_SetAnim(ent, NULL, SETANIM_BOTH, BOTH_STAND1TO2, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD|SETANIM_FLAG_HOLDLESS, 0);
03697                 }
03698                 else
03699                 {
03700                         G_SetAnim(ent, NULL, SETANIM_TORSO, TORSO_RAISEWEAP1, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD|SETANIM_FLAG_HOLDLESS, 0);
03701                         client->ps.legsAnim = WeaponReadyAnim[client->ps.weapon];
03702                 }
03703                 client->ps.weaponstate = WEAPON_RAISING;
03704                 client->ps.weaponTime = client->ps.torsoTimer;
03705         }
03706 
03707         // don't allow full run speed for a bit
03708         client->ps.pm_flags |= PMF_TIME_KNOCKBACK;
03709         client->ps.pm_time = 100;
03710 
03711         client->respawnTime = level.time;
03712         client->inactivityTime = level.time + g_inactivity.integer * 1000;
03713         client->latched_buttons = 0;
03714 
03715         if ( level.intermissiontime ) {
03716                 MoveClientToIntermission( ent );
03717         } else {
03718                 // fire the targets of the spawn point
03719                 G_UseTargets( spawnPoint, ent );
03720 
03721                 // select the highest weapon number available, after any
03722                 // spawn given items have fired
03723                 /*
03724                 client->ps.weapon = 1;
03725                 for ( i = WP_NUM_WEAPONS - 1 ; i > 0 ; i-- ) {
03726                         if ( client->ps.stats[STAT_WEAPONS] & ( 1 << i ) ) {
03727                                 client->ps.weapon = i;
03728                                 break;
03729                         }
03730                 }
03731                 */
03732         }
03733 
03734         //set teams for NPCs to recognize
03735         if (g_gametype.integer == GT_SIEGE)
03736         { //Imperial (team1) team is allied with "enemy" NPCs in this mode
03737                 if (client->sess.sessionTeam == SIEGETEAM_TEAM1)
03738                 {
03739                         client->playerTeam = ent->s.teamowner = NPCTEAM_ENEMY;
03740                         client->enemyTeam = NPCTEAM_PLAYER;
03741                 }
03742                 else
03743                 {
03744                         client->playerTeam = ent->s.teamowner = NPCTEAM_PLAYER;
03745                         client->enemyTeam = NPCTEAM_ENEMY;
03746                 }
03747         }
03748         else
03749         {
03750                 client->playerTeam = ent->s.teamowner = NPCTEAM_PLAYER;
03751                 client->enemyTeam = NPCTEAM_ENEMY;
03752         }
03753 
03754         /*
03755         //scaling for the power duel opponent
03756         if (g_gametype.integer == GT_POWERDUEL &&
03757                 client->sess.duelTeam == DUELTEAM_LONE)
03758         {
03759                 client->ps.iModelScale = 125;
03760                 VectorSet(ent->modelScale, 1.25f, 1.25f, 1.25f);
03761         }
03762         */
03763         //Disabled. At least for now. Not sure if I'll want to do it or not eventually.
03764 
03765         // run a client frame to drop exactly to the floor,
03766         // initialize animations and other things
03767         client->ps.commandTime = level.time - 100;
03768         ent->client->pers.cmd.serverTime = level.time;
03769         ClientThink( ent-g_entities, NULL );
03770 
03771         // positively link the client, even if the command times are weird
03772         if ( ent->client->sess.sessionTeam != TEAM_SPECTATOR ) {
03773                 BG_PlayerStateToEntityState( &client->ps, &ent->s, qtrue );
03774                 VectorCopy( ent->client->ps.origin, ent->r.currentOrigin );
03775                 trap_LinkEntity( ent );
03776         }
03777 
03778         if (g_spawnInvulnerability.integer)
03779         {
03780                 ent->client->ps.eFlags |= EF_INVULNERABLE;
03781                 ent->client->invulnerableTimer = level.time + g_spawnInvulnerability.integer;
03782         }
03783 
03784         // run the presend to set anything else
03785         ClientEndFrame( ent );
03786 
03787         // clear entity state values
03788         BG_PlayerStateToEntityState( &client->ps, &ent->s, qtrue );
03789 
03790         //rww - make sure client has a valid icarus instance
03791         trap_ICARUS_FreeEnt( ent );
03792         trap_ICARUS_InitEnt( ent );
03793 }
03794 
03795 
03796 /*
03797 ===========
03798 ClientDisconnect
03799 
03800 Called when a player drops from the server.
03801 Will not be called between levels.
03802 
03803 This should NOT be called directly by any game logic,
03804 call trap_DropClient(), which will call this and do
03805 server system housekeeping.
03806 ============
03807 */
03808 void ClientDisconnect( int clientNum ) {
03809         gentity_t       *ent;
03810         gentity_t       *tent;
03811         int                     i;
03812 
03813         // cleanup if we are kicking a bot that
03814         // hasn't spawned yet
03815         G_RemoveQueuedBotBegin( clientNum );
03816 
03817         ent = g_entities + clientNum;
03818         if ( !ent->client ) {
03819                 return;
03820         }
03821 
03822         i = 0;
03823 
03824         while (i < NUM_FORCE_POWERS)
03825         {
03826                 if (ent->client->ps.fd.forcePowersActive & (1 << i))
03827                 {
03828                         WP_ForcePowerStop(ent, i);
03829                 }
03830                 i++;
03831         }
03832 
03833         i = TRACK_CHANNEL_1;
03834 
03835         while (i < NUM_TRACK_CHANNELS)
03836         {
03837                 if (ent->client->ps.fd.killSoundEntIndex[i-50] && ent->client->ps.fd.killSoundEntIndex[i-50] < MAX_GENTITIES && ent->client->ps.fd.killSoundEntIndex[i-50] > 0)
03838                 {
03839                         G_MuteSound(ent->client->ps.fd.killSoundEntIndex[i-50], CHAN_VOICE);
03840                 }
03841                 i++;
03842         }
03843         i = 0;
03844 
03845         if (ent->client->ps.m_iVehicleNum)
03846         { //tell it I'm getting off
03847                 gentity_t *veh = &g_entities[ent->client->ps.m_iVehicleNum];
03848 
03849                 if (veh->inuse && veh->client && veh->m_pVehicle)
03850                 {
03851                         int pCon = ent->client->pers.connected;
03852 
03853                         ent->client->pers.connected = 0;
03854                         veh->m_pVehicle->m_pVehicleInfo->Eject(veh->m_pVehicle, (bgEntity_t *)ent, qtrue);
03855                         ent->client->pers.connected = pCon;
03856                 }
03857         }
03858 
03859         // stop any following clients
03860         for ( i = 0 ; i < level.maxclients ; i++ ) {
03861                 if ( level.clients[i].sess.sessionTeam == TEAM_SPECTATOR
03862                         && level.clients[i].sess.spectatorState == SPECTATOR_FOLLOW
03863                         && level.clients[i].sess.spectatorClient == clientNum ) {
03864                         StopFollowing( &g_entities[i] );
03865                 }
03866         }
03867 
03868         // send effect if they were completely connected
03869         if ( ent->client->pers.connected == CON_CONNECTED 
03870                 && ent->client->sess.sessionTeam != TEAM_SPECTATOR ) {
03871                 tent = G_TempEntity( ent->client->ps.origin, EV_PLAYER_TELEPORT_OUT );
03872                 tent->s.clientNum = ent->s.clientNum;
03873 
03874                 // They don't get to take powerups with them!
03875                 // Especially important for stuff like CTF flags
03876                 TossClientItems( ent );
03877         }
03878 
03879         G_LogPrintf( "ClientDisconnect: %i\n", clientNum );
03880 
03881         // if we are playing in tourney mode, give a win to the other player and clear his frags for this round
03882         if ( (g_gametype.integer == GT_DUEL )
03883                 && !level.intermissiontime
03884                 && !level.warmupTime ) {
03885                 if ( level.sortedClients[1] == clientNum ) {
03886                         level.clients[ level.sortedClients[0] ].ps.persistant[PERS_SCORE] = 0;
03887                         level.clients[ level.sortedClients[0] ].sess.wins++;
03888                         ClientUserinfoChanged( level.sortedClients[0] );
03889                 }
03890                 else if ( level.sortedClients[0] == clientNum ) {
03891                         level.clients[ level.sortedClients[1] ].ps.persistant[PERS_SCORE] = 0;
03892                         level.clients[ level.sortedClients[1] ].sess.wins++;
03893                         ClientUserinfoChanged( level.sortedClients[1] );
03894                 }
03895         }
03896 
03897         if (ent->ghoul2 && trap_G2_HaveWeGhoul2Models(ent->ghoul2))
03898         {
03899                 trap_G2API_CleanGhoul2Models(&ent->ghoul2);
03900         }
03901         i = 0;
03902         while (i < MAX_SABERS)
03903         {
03904                 if (ent->client->weaponGhoul2[i] && trap_G2_HaveWeGhoul2Models(ent->client->weaponGhoul2[i]))
03905                 {
03906                         trap_G2API_CleanGhoul2Models(&ent->client->weaponGhoul2[i]);
03907                 }
03908                 i++;
03909         }
03910 
03911         trap_UnlinkEntity (ent);
03912         ent->s.modelindex = 0;
03913         ent->inuse = qfalse;
03914         ent->classname = "disconnected";
03915         ent->client->pers.connected = CON_DISCONNECTED;
03916         ent->client->ps.persistant[PERS_TEAM] = TEAM_FREE;
03917         ent->client->sess.sessionTeam = TEAM_FREE;
03918         ent->r.contents = 0;
03919 
03920         trap_SetConfigstring( CS_PLAYERS + clientNum, "");
03921 
03922         CalculateRanks();
03923 
03924         if ( ent->r.svFlags & SVF_BOT ) {
03925                 BotAIShutdownClient( clientNum, qfalse );
03926         }
03927 
03928         G_ClearClientLog(clientNum);
03929 }
03930 
03931