File: game\p_client.c

    1 /*
    2 Copyright (C) 1997-2001 Id Software, Inc.
    3 
    4 This program is free software; you can redistribute it and/or
    5 modify it under the terms of the GNU General Public License
    6 as published by the Free Software Foundation; either version 2
    7 of the License, or (at your option) any later version.
    8 
    9 This program is distributed in the hope that it will be useful,
   10 but WITHOUT ANY WARRANTY; without even the implied warranty of
   11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
   12 
   13 See the GNU General Public License for more details.
   14 
   15 You should have received a copy of the GNU General Public License
   16 along with this program; if not, write to the Free Software
   17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
   18 
   19 */
   20 #include "g_local.h"
   21 #include "m_player.h"
   22 
   23 void ClientUserinfoChanged (edict_t *ent, char *userinfo);
   24 
   25 void SP_misc_teleporter_dest (edict_t *ent);
   26 
   27 //
   28 // Gross, ugly, disgustuing hack section
   29 //
   30 
   31 // this function is an ugly as hell hack to fix some map flaws
   32 //
   33 // the coop spawn spots on some maps are SNAFU.  There are coop spots
   34 // with the wrong targetname as well as spots with no name at all
   35 //
   36 // we use carnal knowledge of the maps to fix the coop spot targetnames to match
   37 // that of the nearest named single player spot
   38 
   39 static void SP_FixCoopSpots (edict_t *self)
   40 {
   41         edict_t *spot;
   42         vec3_t  d;
   43 
   44         spot = NULL;
   45 
   46         while(1)
   47         {
   48                 spot = G_Find(spot, FOFS(classname), "info_player_start");
   49                 if (!spot)
   50                         return;
   51                 if (!spot->targetname)
   52                         continue;
   53                 VectorSubtract(self->s.origin, spot->s.origin, d);
   54                 if (VectorLength(d) < 384)
   55                 {
   56                         if ((!self->targetname) || Q_stricmp(self->targetname, spot->targetname) != 0)
   57                         {
   58 //                              gi.dprintf("FixCoopSpots changed %s at %s targetname from %s to %s\n", self->classname, vtos(self->s.origin), self->targetname, spot->targetname);
   59                                 self->targetname = spot->targetname;
   60                         }
   61                         return;
   62                 }
   63         }
   64 }
   65 
   66 // now if that one wasn't ugly enough for you then try this one on for size
   67 // some maps don't have any coop spots at all, so we need to create them
   68 // where they should have been
   69 
   70 static void SP_CreateCoopSpots (edict_t *self)
   71 {
   72         edict_t *spot;
   73 
   74         if(Q_stricmp(level.mapname, "security") == 0)
   75         {
   76                 spot = G_Spawn();
   77                 spot->classname = "info_player_coop";
   78                 spot->s.origin[0] = 188 - 64;
   79                 spot->s.origin[1] = -164;
   80                 spot->s.origin[2] = 80;
   81                 spot->targetname = "jail3";
   82                 spot->s.angles[1] = 90;
   83 
   84                 spot = G_Spawn();
   85                 spot->classname = "info_player_coop";
   86                 spot->s.origin[0] = 188 + 64;
   87                 spot->s.origin[1] = -164;
   88                 spot->s.origin[2] = 80;
   89                 spot->targetname = "jail3";
   90                 spot->s.angles[1] = 90;
   91 
   92                 spot = G_Spawn();
   93                 spot->classname = "info_player_coop";
   94                 spot->s.origin[0] = 188 + 128;
   95                 spot->s.origin[1] = -164;
   96                 spot->s.origin[2] = 80;
   97                 spot->targetname = "jail3";
   98                 spot->s.angles[1] = 90;
   99 
  100                 return;
  101         }
  102 }
  103 
  104 
  105 /*QUAKED info_player_start (1 0 0) (-16 -16 -24) (16 16 32)
  106 The normal starting point for a level.
  107 */
  108 void SP_info_player_start(edict_t *self)
  109 {
  110         if (!coop->value)
  111                 return;
  112         if(Q_stricmp(level.mapname, "security") == 0)
  113         {
  114                 // invoke one of our gross, ugly, disgusting hacks
  115                 self->think = SP_CreateCoopSpots;
  116                 self->nextthink = level.time + FRAMETIME;
  117         }
  118 }
  119 
  120 /*QUAKED info_player_deathmatch (1 0 1) (-16 -16 -24) (16 16 32)
  121 potential spawning position for deathmatch games
  122 */
  123 void SP_info_player_deathmatch(edict_t *self)
  124 {
  125         if (!deathmatch->value)
  126         {
  127                 G_FreeEdict (self);
  128                 return;
  129         }
  130         SP_misc_teleporter_dest (self);
  131 }
  132 
  133 /*QUAKED info_player_coop (1 0 1) (-16 -16 -24) (16 16 32)
  134 potential spawning position for coop games
  135 */
  136 
  137 void SP_info_player_coop(edict_t *self)
  138 {
  139         if (!coop->value)
  140         {
  141                 G_FreeEdict (self);
  142                 return;
  143         }
  144 
  145         if((Q_stricmp(level.mapname, "jail2") == 0)   ||
  146            (Q_stricmp(level.mapname, "jail4") == 0)   ||
  147            (Q_stricmp(level.mapname, "mine1") == 0)   ||
  148            (Q_stricmp(level.mapname, "mine2") == 0)   ||
  149            (Q_stricmp(level.mapname, "mine3") == 0)   ||
  150            (Q_stricmp(level.mapname, "mine4") == 0)   ||
  151            (Q_stricmp(level.mapname, "lab") == 0)     ||
  152            (Q_stricmp(level.mapname, "boss1") == 0)   ||
  153            (Q_stricmp(level.mapname, "fact3") == 0)   ||
  154            (Q_stricmp(level.mapname, "biggun") == 0)  ||
  155            (Q_stricmp(level.mapname, "space") == 0)   ||
  156            (Q_stricmp(level.mapname, "command") == 0) ||
  157            (Q_stricmp(level.mapname, "power2") == 0) ||
  158            (Q_stricmp(level.mapname, "strike") == 0))
  159         {
  160                 // invoke one of our gross, ugly, disgusting hacks
  161                 self->think = SP_FixCoopSpots;
  162                 self->nextthink = level.time + FRAMETIME;
  163         }
  164 }
  165 
  166 
  167 /*QUAKED info_player_intermission (1 0 1) (-16 -16 -24) (16 16 32)
  168 The deathmatch intermission point will be at one of these
  169 Use 'angles' instead of 'angle', so you can set pitch or roll as well as yaw.  'pitch yaw roll'
  170 */
  171 void SP_info_player_intermission(void)
  172 {
  173 }
  174 
  175 
  176 //=======================================================================
  177 
  178 
  179 void player_pain (edict_t *self, edict_t *other, float kick, int damage)
  180 {
  181         // player pain is handled at the end of the frame in P_DamageFeedback
  182 }
  183 
  184 
  185 qboolean IsFemale (edict_t *ent)
  186 {
  187         char            *info;
  188 
  189         if (!ent->client)
  190                 return false;
  191 
  192         info = Info_ValueForKey (ent->client->pers.userinfo, "gender");
  193         if (info[0] == 'f' || info[0] == 'F')
  194                 return true;
  195         return false;
  196 }
  197 
  198 qboolean IsNeutral (edict_t *ent)
  199 {
  200         char            *info;
  201 
  202         if (!ent->client)
  203                 return false;
  204 
  205         info = Info_ValueForKey (ent->client->pers.userinfo, "gender");
  206         if (info[0] != 'f' && info[0] != 'F' && info[0] != 'm' && info[0] != 'M')
  207                 return true;
  208         return false;
  209 }
  210 
  211 void ClientObituary (edict_t *self, edict_t *inflictor, edict_t *attacker)
  212 {
  213         int                     mod;
  214         char            *message;
  215         char            *message2;
  216         qboolean        ff;
  217 
  218         if (coop->value && attacker->client)
  219                 meansOfDeath |= MOD_FRIENDLY_FIRE;
  220 
  221         if (deathmatch->value || coop->value)
  222         {
  223                 ff = meansOfDeath & MOD_FRIENDLY_FIRE;
  224                 mod = meansOfDeath & ~MOD_FRIENDLY_FIRE;
  225                 message = NULL;
  226                 message2 = "";
  227 
  228                 switch (mod)
  229                 {
  230                 case MOD_SUICIDE:
  231                         message = "suicides";
  232                         break;
  233                 case MOD_FALLING:
  234                         message = "cratered";
  235                         break;
  236                 case MOD_CRUSH:
  237                         message = "was squished";
  238                         break;
  239                 case MOD_WATER:
  240                         message = "sank like a rock";
  241                         break;
  242                 case MOD_SLIME:
  243                         message = "melted";
  244                         break;
  245                 case MOD_LAVA:
  246                         message = "does a back flip into the lava";
  247                         break;
  248                 case MOD_EXPLOSIVE:
  249                 case MOD_BARREL:
  250                         message = "blew up";
  251                         break;
  252                 case MOD_EXIT:
  253                         message = "found a way out";
  254                         break;
  255                 case MOD_TARGET_LASER:
  256                         message = "saw the light";
  257                         break;
  258                 case MOD_TARGET_BLASTER:
  259                         message = "got blasted";
  260                         break;
  261                 case MOD_BOMB:
  262                 case MOD_SPLASH:
  263                 case MOD_TRIGGER_HURT:
  264                         message = "was in the wrong place";
  265                         break;
  266                 }
  267                 if (attacker == self)
  268                 {
  269                         switch (mod)
  270                         {
  271                         case MOD_HELD_GRENADE:
  272                                 message = "tried to put the pin back in";
  273                                 break;
  274                         case MOD_HG_SPLASH:
  275                         case MOD_G_SPLASH:
  276                                 if (IsNeutral(self))
  277                                         message = "tripped on its own grenade";
  278                                 else if (IsFemale(self))
  279                                         message = "tripped on her own grenade";
  280                                 else
  281                                         message = "tripped on his own grenade";
  282                                 break;
  283                         case MOD_R_SPLASH:
  284                                 if (IsNeutral(self))
  285                                         message = "blew itself up";
  286                                 else if (IsFemale(self))
  287                                         message = "blew herself up";
  288                                 else
  289                                         message = "blew himself up";
  290                                 break;
  291                         case MOD_BFG_BLAST:
  292                                 message = "should have used a smaller gun";
  293                                 break;
  294                         default:
  295                                 if (IsNeutral(self))
  296                                         message = "killed itself";
  297                                 else if (IsFemale(self))
  298                                         message = "killed herself";
  299                                 else
  300                                         message = "killed himself";
  301                                 break;
  302                         }
  303                 }
  304                 if (message)
  305                 {
  306                         gi.bprintf (PRINT_MEDIUM, "%s %s.\n", self->client->pers.netname, message);
  307                         if (deathmatch->value)
  308                                 self->client->resp.score--;
  309                         self->enemy = NULL;
  310                         return;
  311                 }
  312 
  313                 self->enemy = attacker;
  314                 if (attacker && attacker->client)
  315                 {
  316                         switch (mod)
  317                         {
  318                         case MOD_BLASTER:
  319                                 message = "was blasted by";
  320                                 break;
  321                         case MOD_SHOTGUN:
  322                                 message = "was gunned down by";
  323                                 break;
  324                         case MOD_SSHOTGUN:
  325                                 message = "was blown away by";
  326                                 message2 = "'s super shotgun";
  327                                 break;
  328                         case MOD_MACHINEGUN:
  329                                 message = "was machinegunned by";
  330                                 break;
  331                         case MOD_CHAINGUN:
  332                                 message = "was cut in half by";
  333                                 message2 = "'s chaingun";
  334                                 break;
  335                         case MOD_GRENADE:
  336                                 message = "was popped by";
  337                                 message2 = "'s grenade";
  338                                 break;
  339                         case MOD_G_SPLASH:
  340                                 message = "was shredded by";
  341                                 message2 = "'s shrapnel";
  342                                 break;
  343                         case MOD_ROCKET:
  344                                 message = "ate";
  345                                 message2 = "'s rocket";
  346                                 break;
  347                         case MOD_R_SPLASH:
  348                                 message = "almost dodged";
  349                                 message2 = "'s rocket";
  350                                 break;
  351                         case MOD_HYPERBLASTER:
  352                                 message = "was melted by";
  353                                 message2 = "'s hyperblaster";
  354                                 break;
  355                         case MOD_RAILGUN:
  356                                 message = "was railed by";
  357                                 break;
  358                         case MOD_BFG_LASER:
  359                                 message = "saw the pretty lights from";
  360                                 message2 = "'s BFG";
  361                                 break;
  362                         case MOD_BFG_BLAST:
  363                                 message = "was disintegrated by";
  364                                 message2 = "'s BFG blast";
  365                                 break;
  366                         case MOD_BFG_EFFECT:
  367                                 message = "couldn't hide from";
  368                                 message2 = "'s BFG";
  369                                 break;
  370                         case MOD_HANDGRENADE:
  371                                 message = "caught";
  372                                 message2 = "'s handgrenade";
  373                                 break;
  374                         case MOD_HG_SPLASH:
  375                                 message = "didn't see";
  376                                 message2 = "'s handgrenade";
  377                                 break;
  378                         case MOD_HELD_GRENADE:
  379                                 message = "feels";
  380                                 message2 = "'s pain";
  381                                 break;
  382                         case MOD_TELEFRAG:
  383                                 message = "tried to invade";
  384                                 message2 = "'s personal space";
  385                                 break;
  386                         }
  387                         if (message)
  388                         {
  389                                 gi.bprintf (PRINT_MEDIUM,"%s %s %s%s\n", self->client->pers.netname, message, attacker->client->pers.netname, message2);
  390                                 if (deathmatch->value)
  391                                 {
  392                                         if (ff)
  393                                                 attacker->client->resp.score--;
  394                                         else
  395                                                 attacker->client->resp.score++;
  396                                 }
  397                                 return;
  398                         }
  399                 }
  400         }
  401 
  402         gi.bprintf (PRINT_MEDIUM,"%s died.\n", self->client->pers.netname);
  403         if (deathmatch->value)
  404                 self->client->resp.score--;
  405 }
  406 
  407 
  408 void Touch_Item (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf);
  409 
  410 void TossClientWeapon (edict_t *self)
  411 {
  412         gitem_t         *item;
  413         edict_t         *drop;
  414         qboolean        quad;
  415         float           spread;
  416 
  417         if (!deathmatch->value)
  418                 return;
  419 
  420         item = self->client->pers.weapon;
  421         if (! self->client->pers.inventory[self->client->ammo_index] )
  422                 item = NULL;
  423         if (item && (strcmp (item->pickup_name, "Blaster") == 0))
  424                 item = NULL;
  425 
  426         if (!((int)(dmflags->value) & DF_QUAD_DROP))
  427                 quad = false;
  428         else
  429                 quad = (self->client->quad_framenum > (level.framenum + 10));
  430 
  431         if (item && quad)
  432                 spread = 22.5;
  433         else
  434                 spread = 0.0;
  435 
  436         if (item)
  437         {
  438                 self->client->v_angle[YAW] -= spread;
  439                 drop = Drop_Item (self, item);
  440                 self->client->v_angle[YAW] += spread;
  441                 drop->spawnflags = DROPPED_PLAYER_ITEM;
  442         }
  443 
  444         if (quad)
  445         {
  446                 self->client->v_angle[YAW] += spread;
  447                 drop = Drop_Item (self, FindItemByClassname ("item_quad"));
  448                 self->client->v_angle[YAW] -= spread;
  449                 drop->spawnflags |= DROPPED_PLAYER_ITEM;
  450 
  451                 drop->touch = Touch_Item;
  452                 drop->nextthink = level.time + (self->client->quad_framenum - level.framenum) * FRAMETIME;
  453                 drop->think = G_FreeEdict;
  454         }
  455 }
  456 
  457 
  458 /*
  459 ==================
  460 LookAtKiller
  461 ==================
  462 */
  463 void LookAtKiller (edict_t *self, edict_t *inflictor, edict_t *attacker)
  464 {
  465         vec3_t          dir;
  466 
  467         if (attacker && attacker != world && attacker != self)
  468         {
  469                 VectorSubtract (attacker->s.origin, self->s.origin, dir);
  470         }
  471         else if (inflictor && inflictor != world && inflictor != self)
  472         {
  473                 VectorSubtract (inflictor->s.origin, self->s.origin, dir);
  474         }
  475         else
  476         {
  477                 self->client->killer_yaw = self->s.angles[YAW];
  478                 return;
  479         }
  480 
  481         if (dir[0])
  482                 self->client->killer_yaw = 180/M_PI*atan2(dir[1], dir[0]);
  483         else {
  484                 self->client->killer_yaw = 0;
  485                 if (dir[1] > 0)
  486                         self->client->killer_yaw = 90;
  487                 else if (dir[1] < 0)
  488                         self->client->killer_yaw = -90;
  489         }
  490         if (self->client->killer_yaw < 0)
  491                 self->client->killer_yaw += 360;
  492         
  493 
  494 }
  495 
  496 /*
  497 ==================
  498 player_die
  499 ==================
  500 */
  501 void player_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
  502 {
  503         int             n;
  504 
  505         VectorClear (self->avelocity);
  506 
  507         self->takedamage = DAMAGE_YES;
  508         self->movetype = MOVETYPE_TOSS;
  509 
  510         self->s.modelindex2 = 0;        // remove linked weapon model
  511 
  512         self->s.angles[0] = 0;
  513         self->s.angles[2] = 0;
  514 
  515         self->s.sound = 0;
  516         self->client->weapon_sound = 0;
  517 
  518         self->maxs[2] = -8;
  519 
  520 //      self->solid = SOLID_NOT;
  521         self->svflags |= SVF_DEADMONSTER;
  522 
  523         if (!self->deadflag)
  524         {
  525                 self->client->respawn_time = level.time + 1.0;
  526                 LookAtKiller (self, inflictor, attacker);
  527                 self->client->ps.pmove.pm_type = PM_DEAD;
  528                 ClientObituary (self, inflictor, attacker);
  529                 TossClientWeapon (self);
  530                 if (deathmatch->value)
  531                         Cmd_Help_f (self);              // show scores
  532 
  533                 // clear inventory
  534                 // this is kind of ugly, but it's how we want to handle keys in coop
  535                 for (n = 0; n < game.num_items; n++)
  536                 {
  537                         if (coop->value && itemlist[n].flags & IT_KEY)
  538                                 self->client->resp.coop_respawn.inventory[n] = self->client->pers.inventory[n];
  539                         self->client->pers.inventory[n] = 0;
  540                 }
  541         }
  542 
  543         // remove powerups
  544         self->client->quad_framenum = 0;
  545         self->client->invincible_framenum = 0;
  546         self->client->breather_framenum = 0;
  547         self->client->enviro_framenum = 0;
  548         self->flags &= ~FL_POWER_ARMOR;
  549 
  550         if (self->health < -40)
  551         {       // gib
  552                 gi.sound (self, CHAN_BODY, gi.soundindex ("misc/udeath.wav"), 1, ATTN_NORM, 0);
  553                 for (n= 0; n < 4; n++)
  554                         ThrowGib (self, "models/objects/gibs/sm_meat/tris.md2", damage, GIB_ORGANIC);
  555                 ThrowClientHead (self, damage);
  556 
  557                 self->takedamage = DAMAGE_NO;
  558         }
  559         else
  560         {       // normal death
  561                 if (!self->deadflag)
  562                 {
  563                         static int i;
  564 
  565                         i = (i+1)%3;
  566                         // start a death animation
  567                         self->client->anim_priority = ANIM_DEATH;
  568                         if (self->client->ps.pmove.pm_flags & PMF_DUCKED)
  569                         {
  570                                 self->s.frame = FRAME_crdeath1-1;
  571                                 self->client->anim_end = FRAME_crdeath5;
  572                         }
  573                         else switch (i)
  574                         {
  575                         case 0:
  576                                 self->s.frame = FRAME_death101-1;
  577                                 self->client->anim_end = FRAME_death106;
  578                                 break;
  579                         case 1:
  580                                 self->s.frame = FRAME_death201-1;
  581                                 self->client->anim_end = FRAME_death206;
  582                                 break;
  583                         case 2:
  584                                 self->s.frame = FRAME_death301-1;
  585                                 self->client->anim_end = FRAME_death308;
  586                                 break;
  587                         }
  588                         gi.sound (self, CHAN_VOICE, gi.soundindex(va("*death%i.wav", (rand()%4)+1)), 1, ATTN_NORM, 0);
  589                 }
  590         }
  591 
  592         self->deadflag = DEAD_DEAD;
  593 
  594         gi.linkentity (self);
  595 }
  596 
  597 //=======================================================================
  598 
  599 /*
  600 ==============
  601 InitClientPersistant
  602 
  603 This is only called when the game first initializes in single player,
  604 but is called after each death and level change in deathmatch
  605 ==============
  606 */
  607 void InitClientPersistant (gclient_t *client)
  608 {
  609         gitem_t         *item;
  610 
  611         memset (&client->pers, 0, sizeof(client->pers));
  612 
  613         item = FindItem("Blaster");
  614         client->pers.selected_item = ITEM_INDEX(item);
  615         client->pers.inventory[client->pers.selected_item] = 1;
  616 
  617         client->pers.weapon = item;
  618 
  619         client->pers.health                     = 100;
  620         client->pers.max_health         = 100;
  621 
  622         client->pers.max_bullets        = 200;
  623         client->pers.max_shells         = 100;
  624         client->pers.max_rockets        = 50;
  625         client->pers.max_grenades       = 50;
  626         client->pers.max_cells          = 200;
  627         client->pers.max_slugs          = 50;
  628 
  629         client->pers.connected = true;
  630 }
  631 
  632 
  633 void InitClientResp (gclient_t *client)
  634 {
  635         memset (&client->resp, 0, sizeof(client->resp));
  636         client->resp.enterframe = level.framenum;
  637         client->resp.coop_respawn = client->pers;
  638 }
  639 
  640 /*
  641 ==================
  642 SaveClientData
  643 
  644 Some information that should be persistant, like health, 
  645 is still stored in the edict structure, so it needs to
  646 be mirrored out to the client structure before all the
  647 edicts are wiped.
  648 ==================
  649 */
  650 void SaveClientData (void)
  651 {
  652         int             i;
  653         edict_t *ent;
  654 
  655         for (i=0 ; i<game.maxclients ; i++)
  656         {
  657                 ent = &g_edicts[1+i];
  658                 if (!ent->inuse)
  659                         continue;
  660                 game.clients[i].pers.health = ent->health;
  661                 game.clients[i].pers.max_health = ent->max_health;
  662                 game.clients[i].pers.savedFlags = (ent->flags & (FL_GODMODE|FL_NOTARGET|FL_POWER_ARMOR));
  663                 if (coop->value)
  664                         game.clients[i].pers.score = ent->client->resp.score;
  665         }
  666 }
  667 
  668 void FetchClientEntData (edict_t *ent)
  669 {
  670         ent->health = ent->client->pers.health;
  671         ent->max_health = ent->client->pers.max_health;
  672         ent->flags |= ent->client->pers.savedFlags;
  673         if (coop->value)
  674                 ent->client->resp.score = ent->client->pers.score;
  675 }
  676 
  677 
  678 
  679 /*
  680 =======================================================================
  681 
  682   SelectSpawnPoint
  683 
  684 =======================================================================
  685 */
  686 
  687 /*
  688 ================
  689 PlayersRangeFromSpot
  690 
  691 Returns the distance to the nearest player from the given spot
  692 ================
  693 */
  694 float   PlayersRangeFromSpot (edict_t *spot)
  695 {
  696         edict_t *player;
  697         float   bestplayerdistance;
  698         vec3_t  v;
  699         int             n;
  700         float   playerdistance;
  701 
  702 
  703         bestplayerdistance = 9999999;
  704 
  705         for (n = 1; n <= maxclients->value; n++)
  706         {
  707                 player = &g_edicts[n];
  708 
  709                 if (!player->inuse)
  710                         continue;
  711 
  712                 if (player->health <= 0)
  713                         continue;
  714 
  715                 VectorSubtract (spot->s.origin, player->s.origin, v);
  716                 playerdistance = VectorLength (v);
  717 
  718                 if (playerdistance < bestplayerdistance)
  719                         bestplayerdistance = playerdistance;
  720         }
  721 
  722         return bestplayerdistance;
  723 }
  724 
  725 /*
  726 ================
  727 SelectRandomDeathmatchSpawnPoint
  728 
  729 go to a random point, but NOT the two points closest
  730 to other players
  731 ================
  732 */
  733 edict_t *SelectRandomDeathmatchSpawnPoint (void)
  734 {
  735         edict_t *spot, *spot1, *spot2;
  736         int             count = 0;
  737         int             selection;
  738         float   range, range1, range2;
  739 
  740         spot = NULL;
  741         range1 = range2 = 99999;
  742         spot1 = spot2 = NULL;
  743 
  744         while ((spot = G_Find (spot, FOFS(classname), "info_player_deathmatch")) != NULL)
  745         {
  746                 count++;
  747                 range = PlayersRangeFromSpot(spot);
  748                 if (range < range1)
  749                 {
  750                         range1 = range;
  751                         spot1 = spot;
  752                 }
  753                 else if (range < range2)
  754                 {
  755                         range2 = range;
  756                         spot2 = spot;
  757                 }
  758         }
  759 
  760         if (!count)
  761                 return NULL;
  762 
  763         if (count <= 2)
  764         {
  765                 spot1 = spot2 = NULL;
  766         }
  767         else
  768                 count -= 2;
  769 
  770         selection = rand() % count;
  771 
  772         spot = NULL;
  773         do
  774         {
  775                 spot = G_Find (spot, FOFS(classname), "info_player_deathmatch");
  776                 if (spot == spot1 || spot == spot2)
  777                         selection++;
  778         } while(selection--);
  779 
  780         return spot;
  781 }
  782 
  783 /*
  784 ================
  785 SelectFarthestDeathmatchSpawnPoint
  786 
  787 ================
  788 */
  789 edict_t *SelectFarthestDeathmatchSpawnPoint (void)
  790 {
  791         edict_t *bestspot;
  792         float   bestdistance, bestplayerdistance;
  793         edict_t *spot;
  794 
  795 
  796         spot = NULL;
  797         bestspot = NULL;
  798         bestdistance = 0;
  799         while ((spot = G_Find (spot, FOFS(classname), "info_player_deathmatch")) != NULL)
  800         {
  801                 bestplayerdistance = PlayersRangeFromSpot (spot);
  802 
  803                 if (bestplayerdistance > bestdistance)
  804                 {
  805                         bestspot = spot;
  806                         bestdistance = bestplayerdistance;
  807                 }
  808         }
  809 
  810         if (bestspot)
  811         {
  812                 return bestspot;
  813         }
  814 
  815         // if there is a player just spawned on each and every start spot
  816         // we have no choice to turn one into a telefrag meltdown
  817         spot = G_Find (NULL, FOFS(classname), "info_player_deathmatch");
  818 
  819         return spot;
  820 }
  821 
  822 edict_t *SelectDeathmatchSpawnPoint (void)
  823 {
  824         if ( (int)(dmflags->value) & DF_SPAWN_FARTHEST)
  825                 return SelectFarthestDeathmatchSpawnPoint ();
  826         else
  827                 return SelectRandomDeathmatchSpawnPoint ();
  828 }
  829 
  830 
  831 edict_t *SelectCoopSpawnPoint (edict_t *ent)
  832 {
  833         int             index;
  834         edict_t *spot = NULL;
  835         char    *target;
  836 
  837         index = ent->client - game.clients;
  838 
  839         // player 0 starts in normal player spawn point
  840         if (!index)
  841                 return NULL;
  842 
  843         spot = NULL;
  844 
  845         // assume there are four coop spots at each spawnpoint
  846         while (1)
  847         {
  848                 spot = G_Find (spot, FOFS(classname), "info_player_coop");
  849                 if (!spot)
  850                         return NULL;    // we didn't have enough...
  851 
  852                 target = spot->targetname;
  853                 if (!target)
  854                         target = "";
  855                 if ( Q_stricmp(game.spawnpoint, target) == 0 )
  856                 {       // this is a coop spawn point for one of the clients here
  857                         index--;
  858                         if (!index)
  859                                 return spot;            // this is it
  860                 }
  861         }
  862 
  863 
  864         return spot;
  865 }
  866 
  867 
  868 /*
  869 ===========
  870 SelectSpawnPoint
  871 
  872 Chooses a player start, deathmatch start, coop start, etc
  873 ============
  874 */
  875 void    SelectSpawnPoint (edict_t *ent, vec3_t origin, vec3_t angles)
  876 {
  877         edict_t *spot = NULL;
  878 
  879         if (deathmatch->value)
  880                 spot = SelectDeathmatchSpawnPoint ();
  881         else if (coop->value)
  882                 spot = SelectCoopSpawnPoint (ent);
  883 
  884         // find a single player start spot
  885         if (!spot)
  886         {
  887                 while ((spot = G_Find (spot, FOFS(classname), "info_player_start")) != NULL)
  888                 {
  889                         if (!game.spawnpoint[0] && !spot->targetname)
  890                                 break;
  891 
  892                         if (!game.spawnpoint[0] || !spot->targetname)
  893                                 continue;
  894 
  895                         if (Q_stricmp(game.spawnpoint, spot->targetname) == 0)
  896                                 break;
  897                 }
  898 
  899                 if (!spot)
  900                 {
  901                         if (!game.spawnpoint[0])
  902                         {       // there wasn't a spawnpoint without a target, so use any
  903                                 spot = G_Find (spot, FOFS(classname), "info_player_start");
  904                         }
  905                         if (!spot)
  906                                 gi.error ("Couldn't find spawn point %s\n", game.spawnpoint);
  907                 }
  908         }
  909 
  910         VectorCopy (spot->s.origin, origin);
  911         origin[2] += 9;
  912         VectorCopy (spot->s.angles, angles);
  913 }
  914 
  915 //======================================================================
  916 
  917 
  918 void InitBodyQue (void)
  919 {
  920         int             i;
  921         edict_t *ent;
  922 
  923         level.body_que = 0;
  924         for (i=0; i<BODY_QUEUE_SIZE ; i++)
  925         {
  926                 ent = G_Spawn();
  927                 ent->classname = "bodyque";
  928         }
  929 }
  930 
  931 void body_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
  932 {
  933         int     n;
  934 
  935         if (self->health < -40)
  936         {
  937                 gi.sound (self, CHAN_BODY, gi.soundindex ("misc/udeath.wav"), 1, ATTN_NORM, 0);
  938                 for (n= 0; n < 4; n++)
  939                         ThrowGib (self, "models/objects/gibs/sm_meat/tris.md2", damage, GIB_ORGANIC);
  940                 self->s.origin[2] -= 48;
  941                 ThrowClientHead (self, damage);
  942                 self->takedamage = DAMAGE_NO;
  943         }
  944 }
  945 
  946 void CopyToBodyQue (edict_t *ent)
  947 {
  948         edict_t         *body;
  949 
  950         // grab a body que and cycle to the next one
  951         body = &g_edicts[(int)maxclients->value + level.body_que + 1];
  952         level.body_que = (level.body_que + 1) % BODY_QUEUE_SIZE;
  953 
  954         // FIXME: send an effect on the removed body
  955 
  956         gi.unlinkentity (ent);
  957 
  958         gi.unlinkentity (body);
  959         body->s = ent->s;
  960         body->s.number = body - g_edicts;
  961 
  962         body->svflags = ent->svflags;
  963         VectorCopy (ent->mins, body->mins);
  964         VectorCopy (ent->maxs, body->maxs);
  965         VectorCopy (ent->absmin, body->absmin);
  966         VectorCopy (ent->absmax, body->absmax);
  967         VectorCopy (ent->size, body->size);
  968         body->solid = ent->solid;
  969         body->clipmask = ent->clipmask;
  970         body->owner = ent->owner;
  971         body->movetype = ent->movetype;
  972 
  973         body->die = body_die;
  974         body->takedamage = DAMAGE_YES;
  975 
  976         gi.linkentity (body);
  977 }
  978 
  979 
  980 void respawn (edict_t *self)
  981 {
  982         if (deathmatch->value || coop->value)
  983         {
  984                 // spectator's don't leave bodies
  985                 if (self->movetype != MOVETYPE_NOCLIP)
  986                         CopyToBodyQue (self);
  987                 self->svflags &= ~SVF_NOCLIENT;
  988                 PutClientInServer (self);
  989 
  990                 // add a teleportation effect
  991                 self->s.event = EV_PLAYER_TELEPORT;
  992 
  993                 // hold in place briefly
  994                 self->client->ps.pmove.pm_flags = PMF_TIME_TELEPORT;
  995                 self->client->ps.pmove.pm_time = 14;
  996 
  997                 self->client->respawn_time = level.time;
  998 
  999                 return;
 1000         }
 1001 
 1002         // restart the entire server
 1003         gi.AddCommandString ("menu_loadgame\n");
 1004 }
 1005 
 1006 /* 
 1007  * only called when pers.spectator changes
 1008  * note that resp.spectator should be the opposite of pers.spectator here
 1009  */
 1010 void spectator_respawn (edict_t *ent)
 1011 {
 1012         int i, numspec;
 1013 
 1014         // if the user wants to become a spectator, make sure he doesn't
 1015         // exceed max_spectators
 1016 
 1017         if (ent->client->pers.spectator) {
 1018                 char *value = Info_ValueForKey (ent->client->pers.userinfo, "spectator");
 1019                 if (*spectator_password->string && 
 1020                         strcmp(spectator_password->string, "none") && 
 1021                         strcmp(spectator_password->string, value)) {
 1022                         gi.cprintf(ent, PRINT_HIGH, "Spectator password incorrect.\n");
 1023                         ent->client->pers.spectator = false;
 1024                         gi.WriteByte (svc_stufftext);
 1025                         gi.WriteString ("spectator 0\n");
 1026                         gi.unicast(ent, true);
 1027                         return;
 1028                 }
 1029 
 1030                 // count spectators
 1031                 for (i = 1, numspec = 0; i <= maxclients->value; i++)
 1032                         if (g_edicts[i].inuse && g_edicts[i].client->pers.spectator)
 1033                                 numspec++;
 1034 
 1035                 if (numspec >= maxspectators->value) {
 1036                         gi.cprintf(ent, PRINT_HIGH, "Server spectator limit is full.");
 1037                         ent->client->pers.spectator = false;
 1038                         // reset his spectator var
 1039                         gi.WriteByte (svc_stufftext);
 1040                         gi.WriteString ("spectator 0\n");
 1041                         gi.unicast(ent, true);
 1042                         return;
 1043                 }
 1044         } else {
 1045                 // he was a spectator and wants to join the game
 1046                 // he must have the right password
 1047                 char *value = Info_ValueForKey (ent->client->pers.userinfo, "password");
 1048                 if (*password->string && strcmp(password->string, "none") && 
 1049                         strcmp(password->string, value)) {
 1050                         gi.cprintf(ent, PRINT_HIGH, "Password incorrect.\n");
 1051                         ent->client->pers.spectator = true;
 1052                         gi.WriteByte (svc_stufftext);
 1053                         gi.WriteString ("spectator 1\n");
 1054                         gi.unicast(ent, true);
 1055                         return;
 1056                 }
 1057         }
 1058 
 1059         // clear client on respawn
 1060         ent->client->resp.score = ent->client->pers.score = 0;
 1061 
 1062         ent->svflags &= ~SVF_NOCLIENT;
 1063         PutClientInServer (ent);
 1064 
 1065         // add a teleportation effect
 1066         if (!ent->client->pers.spectator)  {
 1067                 // send effect
 1068                 gi.WriteByte (svc_muzzleflash);
 1069                 gi.WriteShort (ent-g_edicts);
 1070                 gi.WriteByte (MZ_LOGIN);
 1071                 gi.multicast (ent->s.origin, MULTICAST_PVS);
 1072 
 1073                 // hold in place briefly
 1074                 ent->client->ps.pmove.pm_flags = PMF_TIME_TELEPORT;
 1075                 ent->client->ps.pmove.pm_time = 14;
 1076         }
 1077 
 1078         ent->client->respawn_time = level.time;
 1079 
 1080         if (ent->client->pers.spectator) 
 1081                 gi.bprintf (PRINT_HIGH, "%s has moved to the sidelines\n", ent->client->pers.netname);
 1082         else
 1083                 gi.bprintf (PRINT_HIGH, "%s joined the game\n", ent->client->pers.netname);
 1084 }
 1085 
 1086 //==============================================================
 1087 
 1088 
 1089 /*
 1090 ===========
 1091 PutClientInServer
 1092 
 1093 Called when a player connects to a server or respawns in
 1094 a deathmatch.
 1095 ============
 1096 */
 1097 void PutClientInServer (edict_t *ent)
 1098 {
 1099         vec3_t  mins = {-16, -16, -24};
 1100         vec3_t  maxs = {16, 16, 32};
 1101         int             index;
 1102         vec3_t  spawn_origin, spawn_angles;
 1103         gclient_t       *client;
 1104         int             i;
 1105         client_persistant_t     saved;
 1106         client_respawn_t        resp;
 1107 
 1108         // find a spawn point
 1109         // do it before setting health back up, so farthest
 1110         // ranging doesn't count this client
 1111         SelectSpawnPoint (ent, spawn_origin, spawn_angles);
 1112 
 1113         index = ent-g_edicts-1;
 1114         client = ent->client;
 1115 
 1116         // deathmatch wipes most client data every spawn
 1117         if (deathmatch->value)
 1118         {
 1119                 char            userinfo[MAX_INFO_STRING];
 1120 
 1121                 resp = client->resp;
 1122                 memcpy (userinfo, client->pers.userinfo, sizeof(userinfo));
 1123                 InitClientPersistant (client);
 1124                 ClientUserinfoChanged (ent, userinfo);
 1125         }
 1126         else if (coop->value)
 1127         {
 1128 //              int                     n;
 1129                 char            userinfo[MAX_INFO_STRING];
 1130 
 1131                 resp = client->resp;
 1132                 memcpy (userinfo, client->pers.userinfo, sizeof(userinfo));
 1133                 // this is kind of ugly, but it's how we want to handle keys in coop
 1134 //              for (n = 0; n < game.num_items; n++)
 1135 //              {
 1136 //                      if (itemlist[n].flags & IT_KEY)
 1137 //                              resp.coop_respawn.inventory[n] = client->pers.inventory[n];
 1138 //              }
 1139                 resp.coop_respawn.game_helpchanged = client->pers.game_helpchanged;
 1140                 resp.coop_respawn.helpchanged = client->pers.helpchanged;
 1141                 client->pers = resp.coop_respawn;
 1142                 ClientUserinfoChanged (ent, userinfo);
 1143                 if (resp.score > client->pers.score)
 1144                         client->pers.score = resp.score;
 1145         }
 1146         else
 1147         {
 1148                 memset (&resp, 0, sizeof(resp));
 1149         }
 1150 
 1151         // clear everything but the persistant data
 1152         saved = client->pers;
 1153         memset (client, 0, sizeof(*client));
 1154         client->pers = saved;
 1155         if (client->pers.health <= 0)
 1156                 InitClientPersistant(client);
 1157         client->resp = resp;
 1158 
 1159         // copy some data from the client to the entity
 1160         FetchClientEntData (ent);
 1161 
 1162         // clear entity values
 1163         ent->groundentity = NULL;
 1164         ent->client = &game.clients[index];
 1165         ent->takedamage = DAMAGE_AIM;
 1166         ent->movetype = MOVETYPE_WALK;
 1167         ent->viewheight = 22;
 1168         ent->inuse = true;
 1169         ent->classname = "player";
 1170         ent->mass = 200;
 1171         ent->solid = SOLID_BBOX;
 1172         ent->deadflag = DEAD_NO;
 1173         ent->air_finished = level.time + 12;
 1174         ent->clipmask = MASK_PLAYERSOLID;
 1175         ent->model = "players/male/tris.md2";
 1176         ent->pain = player_pain;
 1177         ent->die = player_die;
 1178         ent->waterlevel = 0;
 1179         ent->watertype = 0;
 1180         ent->flags &= ~FL_NO_KNOCKBACK;
 1181         ent->svflags &= ~SVF_DEADMONSTER;
 1182 
 1183         VectorCopy (mins, ent->mins);
 1184         VectorCopy (maxs, ent->maxs);
 1185         VectorClear (ent->velocity);
 1186 
 1187         // clear playerstate values
 1188         memset (&ent->client->ps, 0, sizeof(client->ps));
 1189 
 1190         client->ps.pmove.origin[0] = spawn_origin[0]*8;
 1191         client->ps.pmove.origin[1] = spawn_origin[1]*8;
 1192         client->ps.pmove.origin[2] = spawn_origin[2]*8;
 1193 
 1194         if (deathmatch->value && ((int)dmflags->value & DF_FIXED_FOV))
 1195         {
 1196                 client->ps.fov = 90;
 1197         }
 1198         else
 1199         {
 1200                 client->ps.fov = atoi(Info_ValueForKey(client->pers.userinfo, "fov"));
 1201                 if (client->ps.fov < 1)
 1202                         client->ps.fov = 90;
 1203                 else if (client->ps.fov > 160)
 1204                         client->ps.fov = 160;
 1205         }
 1206 
 1207         client->ps.gunindex = gi.modelindex(client->pers.weapon->view_model);
 1208 
 1209         // clear entity state values
 1210         ent->s.effects = 0;
 1211         ent->s.modelindex = 255;                // will use the skin specified model
 1212         ent->s.modelindex2 = 255;               // custom gun model
 1213         // sknum is player num and weapon number
 1214         // weapon number will be added in changeweapon
 1215         ent->s.skinnum = ent - g_edicts - 1;
 1216 
 1217         ent->s.frame = 0;
 1218         VectorCopy (spawn_origin, ent->s.origin);
 1219         ent->s.origin[2] += 1;  // make sure off ground
 1220         VectorCopy (ent->s.origin, ent->s.old_origin);
 1221 
 1222         // set the delta angle
 1223         for (i=0 ; i<3 ; i++)
 1224         {
 1225                 client->ps.pmove.delta_angles[i] = ANGLE2SHORT(spawn_angles[i] - client->resp.cmd_angles[i]);
 1226         }
 1227 
 1228         ent->s.angles[PITCH] = 0;
 1229         ent->s.angles[YAW] = spawn_angles[YAW];
 1230         ent->s.angles[ROLL] = 0;
 1231         VectorCopy (ent->s.angles, client->ps.viewangles);
 1232         VectorCopy (ent->s.angles, client->v_angle);
 1233 
 1234         // spawn a spectator
 1235         if (client->pers.spectator) {
 1236                 client->chase_target = NULL;
 1237 
 1238                 client->resp.spectator = true;
 1239 
 1240                 ent->movetype = MOVETYPE_NOCLIP;
 1241                 ent->solid = SOLID_NOT;
 1242                 ent->svflags |= SVF_NOCLIENT;
 1243                 ent->client->ps.gunindex = 0;
 1244                 gi.linkentity (ent);
 1245                 return;
 1246         } else
 1247                 client->resp.spectator = false;
 1248 
 1249         if (!KillBox (ent))
 1250         {       // could't spawn in?
 1251         }
 1252 
 1253         gi.linkentity (ent);
 1254 
 1255         // force the current weapon up
 1256         client->newweapon = client->pers.weapon;
 1257         ChangeWeapon (ent);
 1258 }
 1259 
 1260 /*
 1261 =====================
 1262 ClientBeginDeathmatch
 1263 
 1264 A client has just connected to the server in 
 1265 deathmatch mode, so clear everything out before starting them.
 1266 =====================
 1267 */
 1268 void ClientBeginDeathmatch (edict_t *ent)
 1269 {
 1270         G_InitEdict (ent);
 1271 
 1272         InitClientResp (ent->client);
 1273 
 1274         // locate ent at a spawn point
 1275         PutClientInServer (ent);
 1276 
 1277         if (level.intermissiontime)
 1278         {
 1279                 MoveClientToIntermission (ent);
 1280         }
 1281         else
 1282         {
 1283                 // send effect
 1284                 gi.WriteByte (svc_muzzleflash);
 1285                 gi.WriteShort (ent-g_edicts);
 1286                 gi.WriteByte (MZ_LOGIN);
 1287                 gi.multicast (ent->s.origin, MULTICAST_PVS);
 1288         }
 1289 
 1290         gi.bprintf (PRINT_HIGH, "%s entered the game\n", ent->client->pers.netname);
 1291 
 1292         // make sure all view stuff is valid
 1293         ClientEndServerFrame (ent);
 1294 }
 1295 
 1296 
 1297 /*
 1298 ===========
 1299 ClientBegin
 1300 
 1301 called when a client has finished connecting, and is ready
 1302 to be placed into the game.  This will happen every level load.
 1303 ============
 1304 */
 1305 void ClientBegin (edict_t *ent)
 1306 {
 1307         int             i;
 1308 
 1309         ent->client = game.clients + (ent - g_edicts - 1);
 1310 
 1311         if (deathmatch->value)
 1312         {
 1313                 ClientBeginDeathmatch (ent);
 1314                 return;
 1315         }
 1316 
 1317         // if there is already a body waiting for us (a loadgame), just
 1318         // take it, otherwise spawn one from scratch
 1319         if (ent->inuse == true)
 1320         {
 1321                 // the client has cleared the client side viewangles upon
 1322                 // connecting to the server, which is different than the
 1323                 // state when the game is saved, so we need to compensate
 1324                 // with deltaangles
 1325                 for (i=0 ; i<3 ; i++)
 1326                         ent->client->ps.pmove.delta_angles[i] = ANGLE2SHORT(ent->client->ps.viewangles[i]);
 1327         }
 1328         else
 1329         {
 1330                 // a spawn point will completely reinitialize the entity
 1331                 // except for the persistant data that was initialized at
 1332                 // ClientConnect() time
 1333                 G_InitEdict (ent);
 1334                 ent->classname = "player";
 1335                 InitClientResp (ent->client);
 1336                 PutClientInServer (ent);
 1337         }
 1338 
 1339         if (level.intermissiontime)
 1340         {
 1341                 MoveClientToIntermission (ent);
 1342         }
 1343         else
 1344         {
 1345                 // send effect if in a multiplayer game
 1346                 if (game.maxclients > 1)
 1347                 {
 1348                         gi.WriteByte (svc_muzzleflash);
 1349                         gi.WriteShort (ent-g_edicts);
 1350                         gi.WriteByte (MZ_LOGIN);
 1351                         gi.multicast (ent->s.origin, MULTICAST_PVS);
 1352 
 1353                         gi.bprintf (PRINT_HIGH, "%s entered the game\n", ent->client->pers.netname);
 1354                 }
 1355         }
 1356 
 1357         // make sure all view stuff is valid
 1358         ClientEndServerFrame (ent);
 1359 }
 1360 
 1361 /*
 1362 ===========
 1363 ClientUserInfoChanged
 1364 
 1365 called whenever the player updates a userinfo variable.
 1366 
 1367 The game can override any of the settings in place
 1368 (forcing skins or names, etc) before copying it off.
 1369 ============
 1370 */
 1371 void ClientUserinfoChanged (edict_t *ent, char *userinfo)
 1372 {
 1373         char    *s;
 1374         int             playernum;
 1375 
 1376         // check for malformed or illegal info strings
 1377         if (!Info_Validate(userinfo))
 1378         {
 1379                 strcpy (userinfo, "\\name\\badinfo\\skin\\male/grunt");
 1380         }
 1381 
 1382         // set name
 1383         s = Info_ValueForKey (userinfo, "name");
 1384         strncpy (ent->client->pers.netname, s, sizeof(ent->client->pers.netname)-1);
 1385 
 1386         // set spectator
 1387         s = Info_ValueForKey (userinfo, "spectator");
 1388         // spectators are only supported in deathmatch
 1389         if (deathmatch->value && *s && strcmp(s, "0"))
 1390                 ent->client->pers.spectator = true;
 1391         else
 1392                 ent->client->pers.spectator = false;
 1393 
 1394         // set skin
 1395         s = Info_ValueForKey (userinfo, "skin");
 1396 
 1397         playernum = ent-g_edicts-1;
 1398 
 1399         // combine name and skin into a configstring
 1400         gi.configstring (CS_PLAYERSKINS+playernum, va("%s\\%s", ent->client->pers.netname, s) );
 1401 
 1402         // fov
 1403         if (deathmatch->value && ((int)dmflags->value & DF_FIXED_FOV))
 1404         {
 1405                 ent->client->ps.fov = 90;
 1406         }
 1407         else
 1408         {
 1409                 ent->client->ps.fov = atoi(Info_ValueForKey(userinfo, "fov"));
 1410                 if (ent->client->ps.fov < 1)
 1411                         ent->client->ps.fov = 90;
 1412                 else if (ent->client->ps.fov > 160)
 1413                         ent->client->ps.fov = 160;
 1414         }
 1415 
 1416         // handedness
 1417         s = Info_ValueForKey (userinfo, "hand");
 1418         if (strlen(s))
 1419         {
 1420                 ent->client->pers.hand = atoi(s);
 1421         }
 1422 
 1423         // save off the userinfo in case we want to check something later
 1424         strncpy (ent->client->pers.userinfo, userinfo, sizeof(ent->client->pers.userinfo)-1);
 1425 }
 1426 
 1427 
 1428 /*
 1429 ===========
 1430 ClientConnect
 1431 
 1432 Called when a player begins connecting to the server.
 1433 The game can refuse entrance to a client by returning false.
 1434 If the client is allowed, the connection process will continue
 1435 and eventually get to ClientBegin()
 1436 Changing levels will NOT cause this to be called again, but
 1437 loadgames will.
 1438 ============
 1439 */
 1440 qboolean ClientConnect (edict_t *ent, char *userinfo)
 1441 {
 1442         char    *value;
 1443 
 1444         // check to see if they are on the banned IP list
 1445         value = Info_ValueForKey (userinfo, "ip");
 1446         if (SV_FilterPacket(value)) {
 1447                 Info_SetValueForKey(userinfo, "rejmsg", "Banned.");
 1448                 return false;
 1449         }
 1450 
 1451         // check for a spectator
 1452         value = Info_ValueForKey (userinfo, "spectator");
 1453         if (deathmatch->value && *value && strcmp(value, "0")) {
 1454                 int i, numspec;
 1455 
 1456                 if (*spectator_password->string && 
 1457                         strcmp(spectator_password->string, "none") && 
 1458                         strcmp(spectator_password->string, value)) {
 1459                         Info_SetValueForKey(userinfo, "rejmsg", "Spectator password required or incorrect.");
 1460                         return false;
 1461                 }
 1462 
 1463                 // count spectators
 1464                 for (i = numspec = 0; i < maxclients->value; i++)
 1465                         if (g_edicts[i+1].inuse && g_edicts[i+1].client->pers.spectator)
 1466                                 numspec++;
 1467 
 1468                 if (numspec >= maxspectators->value) {
 1469                         Info_SetValueForKey(userinfo, "rejmsg", "Server spectator limit is full.");
 1470                         return false;
 1471                 }
 1472         } else {
 1473                 // check for a password
 1474                 value = Info_ValueForKey (userinfo, "password");
 1475                 if (*password->string && strcmp(password->string, "none") && 
 1476                         strcmp(password->string, value)) {
 1477                         Info_SetValueForKey(userinfo, "rejmsg", "Password required or incorrect.");
 1478                         return false;
 1479                 }
 1480         }
 1481 
 1482 
 1483         // they can connect
 1484         ent->client = game.clients + (ent - g_edicts - 1);
 1485 
 1486         // if there is already a body waiting for us (a loadgame), just
 1487         // take it, otherwise spawn one from scratch
 1488         if (ent->inuse == false)
 1489         {
 1490                 // clear the respawning variables
 1491                 InitClientResp (ent->client);
 1492                 if (!game.autosaved || !ent->client->pers.weapon)
 1493                         InitClientPersistant (ent->client);
 1494         }
 1495 
 1496         ClientUserinfoChanged (ent, userinfo);
 1497 
 1498         if (game.maxclients > 1)
 1499                 gi.dprintf ("%s connected\n", ent->client->pers.netname);
 1500 
 1501         ent->svflags = 0; // make sure we start with known default
 1502         ent->client->pers.connected = true;
 1503         return true;
 1504 }
 1505 
 1506 /*
 1507 ===========
 1508 ClientDisconnect
 1509 
 1510 Called when a player drops from the server.
 1511 Will not be called between levels.
 1512 ============
 1513 */
 1514 void ClientDisconnect (edict_t *ent)
 1515 {
 1516         int             playernum;
 1517 
 1518         if (!ent->client)
 1519                 return;
 1520 
 1521         gi.bprintf (PRINT_HIGH, "%s disconnected\n", ent->client->pers.netname);
 1522 
 1523         // send effect
 1524         gi.WriteByte (svc_muzzleflash);
 1525         gi.WriteShort (ent-g_edicts);
 1526         gi.WriteByte (MZ_LOGOUT);
 1527         gi.multicast (ent->s.origin, MULTICAST_PVS);
 1528 
 1529         gi.unlinkentity (ent);
 1530         ent->s.modelindex = 0;
 1531         ent->solid = SOLID_NOT;
 1532         ent->inuse = false;
 1533         ent->classname = "disconnected";
 1534         ent->client->pers.connected = false;
 1535 
 1536         playernum = ent-g_edicts-1;
 1537         gi.configstring (CS_PLAYERSKINS+playernum, "");
 1538 }
 1539 
 1540 
 1541 //==============================================================
 1542 
 1543 
 1544 edict_t *pm_passent;
 1545 
 1546 // pmove doesn't need to know about passent and contentmask
 1547 trace_t PM_trace (vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end)
 1548 {
 1549         if (pm_passent->health > 0)
 1550                 return gi.trace (start, mins, maxs, end, pm_passent, MASK_PLAYERSOLID);
 1551         else
 1552                 return gi.trace (start, mins, maxs, end, pm_passent, MASK_DEADSOLID);
 1553 }
 1554 
 1555 unsigned CheckBlock (void *b, int c)
 1556 {
 1557         int     v,i;
 1558         v = 0;
 1559         for (i=0 ; i<c ; i++)
 1560                 v+= ((byte *)b)[i];
 1561         return v;
 1562 }
 1563 void PrintPmove (pmove_t *pm)
 1564 {
 1565         unsigned        c1, c2;
 1566 
 1567         c1 = CheckBlock (&pm->s, sizeof(pm->s));
 1568         c2 = CheckBlock (&pm->cmd, sizeof(pm->cmd));
 1569         Com_Printf ("sv %3i:%i %i\n", pm->cmd.impulse, c1, c2);
 1570 }
 1571 
 1572 /*
 1573 ==============
 1574 ClientThink
 1575 
 1576 This will be called once for each client frame, which will
 1577 usually be a couple times for each server frame.
 1578 ==============
 1579 */
 1580 void ClientThink (edict_t *ent, usercmd_t *ucmd)
 1581 {
 1582         gclient_t       *client;
 1583         edict_t *other;
 1584         int             i, j;
 1585         pmove_t pm;
 1586 
 1587         level.current_entity = ent;
 1588         client = ent->client;
 1589 
 1590         if (level.intermissiontime)
 1591         {
 1592                 client->ps.pmove.pm_type = PM_FREEZE;
 1593                 // can exit intermission after five seconds
 1594                 if (level.time > level.intermissiontime + 5.0 
 1595                         && (ucmd->buttons & BUTTON_ANY) )
 1596                         level.exitintermission = true;
 1597                 return;
 1598         }
 1599 
 1600         pm_passent = ent;
 1601 
 1602         if (ent->client->chase_target) {
 1603 
 1604                 client->resp.cmd_angles[0] = SHORT2ANGLE(ucmd->angles[0]);
 1605                 client->resp.cmd_angles[1] = SHORT2ANGLE(ucmd->angles[1]);
 1606                 client->resp.cmd_angles[2] = SHORT2ANGLE(ucmd->angles[2]);
 1607 
 1608         } else {
 1609 
 1610                 // set up for pmove
 1611                 memset (&pm, 0, sizeof(pm));
 1612 
 1613                 if (ent->movetype == MOVETYPE_NOCLIP)
 1614                         client->ps.pmove.pm_type = PM_SPECTATOR;
 1615                 else if (ent->s.modelindex != 255)
 1616                         client->ps.pmove.pm_type = PM_GIB;
 1617                 else if (ent->deadflag)
 1618                         client->ps.pmove.pm_type = PM_DEAD;
 1619                 else
 1620                         client->ps.pmove.pm_type = PM_NORMAL;
 1621 
 1622                 client->ps.pmove.gravity = sv_gravity->value;
 1623                 pm.s = client->ps.pmove;
 1624 
 1625                 for (i=0 ; i<3 ; i++)
 1626                 {
 1627                         pm.s.origin[i] = ent->s.origin[i]*8;
 1628                         pm.s.velocity[i] = ent->velocity[i]*8;
 1629                 }
 1630 
 1631                 if (memcmp(&client->old_pmove, &pm.s, sizeof(pm.s)))
 1632                 {
 1633                         pm.snapinitial = true;
 1634         //              gi.dprintf ("pmove changed!\n");
 1635                 }
 1636 
 1637                 pm.cmd = *ucmd;
 1638 
 1639                 pm.trace = PM_trace;    // adds default parms
 1640                 pm.pointcontents = gi.pointcontents;
 1641 
 1642                 // perform a pmove
 1643                 gi.Pmove (&pm);
 1644 
 1645                 // save results of pmove
 1646                 client->ps.pmove = pm.s;
 1647                 client->old_pmove = pm.s;
 1648 
 1649                 for (i=0 ; i<3 ; i++)
 1650                 {
 1651                         ent->s.origin[i] = pm.s.origin[i]*0.125;
 1652                         ent->velocity[i] = pm.s.velocity[i]*0.125;
 1653                 }
 1654 
 1655                 VectorCopy (pm.mins, ent->mins);
 1656                 VectorCopy (pm.maxs, ent->maxs);
 1657 
 1658                 client->resp.cmd_angles[0] = SHORT2ANGLE(ucmd->angles[0]);
 1659                 client->resp.cmd_angles[1] = SHORT2ANGLE(ucmd->angles[1]);
 1660                 client->resp.cmd_angles[2] = SHORT2ANGLE(ucmd->angles[2]);
 1661 
 1662                 if (ent->groundentity && !pm.groundentity && (pm.cmd.upmove >= 10) && (pm.waterlevel == 0))
 1663                 {
 1664                         gi.sound(ent, CHAN_VOICE, gi.soundindex("*jump1.wav"), 1, ATTN_NORM, 0);
 1665                         PlayerNoise(ent, ent->s.origin, PNOISE_SELF);
 1666                 }
 1667 
 1668                 ent->viewheight = pm.viewheight;
 1669                 ent->waterlevel = pm.waterlevel;
 1670                 ent->watertype = pm.watertype;
 1671                 ent->groundentity = pm.groundentity;
 1672                 if (pm.groundentity)
 1673                         ent->groundentity_linkcount = pm.groundentity->linkcount;
 1674 
 1675                 if (ent->deadflag)
 1676                 {
 1677                         client->ps.viewangles[ROLL] = 40;
 1678                         client->ps.viewangles[PITCH] = -15;
 1679                         client->ps.viewangles[YAW] = client->killer_yaw;
 1680                 }
 1681                 else
 1682                 {
 1683                         VectorCopy (pm.viewangles, client->v_angle);
 1684                         VectorCopy (pm.viewangles, client->ps.viewangles);
 1685                 }
 1686 
 1687                 gi.linkentity (ent);
 1688 
 1689                 if (ent->movetype != MOVETYPE_NOCLIP)
 1690                         G_TouchTriggers (ent);
 1691 
 1692                 // touch other objects
 1693                 for (i=0 ; i<pm.numtouch ; i++)
 1694                 {
 1695                         other = pm.touchents[i];
 1696                         for (j=0 ; j<i ; j++)
 1697                                 if (pm.touchents[j] == other)
 1698                                         break;
 1699                         if (j != i)
 1700                                 continue;       // duplicated
 1701                         if (!other->touch)
 1702                                 continue;
 1703                         other->touch (other, ent, NULL, NULL);
 1704                 }
 1705 
 1706         }
 1707 
 1708         client->oldbuttons = client->buttons;
 1709         client->buttons = ucmd->buttons;
 1710         client->latched_buttons |= client->buttons & ~client->oldbuttons;
 1711 
 1712         // save light level the player is standing on for
 1713         // monster sighting AI
 1714         ent->light_level = ucmd->lightlevel;
 1715 
 1716         // fire weapon from final position if needed
 1717         if (client->latched_buttons & BUTTON_ATTACK)
 1718         {
 1719                 if (client->resp.spectator) {
 1720 
 1721                         client->latched_buttons = 0;
 1722 
 1723                         if (client->chase_target) {
 1724                                 client->chase_target = NULL;
 1725                                 client->ps.pmove.pm_flags &= ~PMF_NO_PREDICTION;
 1726                         } else
 1727                                 GetChaseTarget(ent);
 1728 
 1729                 } else if (!client->weapon_thunk) {
 1730                         client->weapon_thunk = true;
 1731                         Think_Weapon (ent);
 1732                 }
 1733         }
 1734 
 1735         if (client->resp.spectator) {
 1736                 if (ucmd->upmove >= 10) {
 1737                         if (!(client->ps.pmove.pm_flags & PMF_JUMP_HELD)) {
 1738                                 client->ps.pmove.pm_flags |= PMF_JUMP_HELD;
 1739                                 if (client->chase_target)
 1740                                         ChaseNext(ent);
 1741                                 else
 1742                                         GetChaseTarget(ent);
 1743                         }
 1744                 } else
 1745                         client->ps.pmove.pm_flags &= ~PMF_JUMP_HELD;
 1746         }
 1747 
 1748         // update chase cam if being followed
 1749         for (i = 1; i <= maxclients->value; i++) {
 1750                 other = g_edicts + i;
 1751                 if (other->inuse && other->client->chase_target == ent)
 1752                         UpdateChaseCam(other);
 1753         }
 1754 }
 1755 
 1756 
 1757 /*
 1758 ==============
 1759 ClientBeginServerFrame
 1760 
 1761 This will be called once for each server frame, before running
 1762 any other entities in the world.
 1763 ==============
 1764 */
 1765 void ClientBeginServerFrame (edict_t *ent)
 1766 {
 1767         gclient_t       *client;
 1768         int                     buttonMask;
 1769 
 1770         if (level.intermissiontime)
 1771                 return;
 1772 
 1773         client = ent->client;
 1774 
 1775         if (deathmatch->value &&
 1776                 client->pers.spectator != client->resp.spectator &&
 1777                 (level.time - client->respawn_time) >= 5) {
 1778                 spectator_respawn(ent);
 1779                 return;
 1780         }
 1781 
 1782         // run weapon animations if it hasn't been done by a ucmd_t
 1783         if (!client->weapon_thunk && !client->resp.spectator)
 1784                 Think_Weapon (ent);
 1785         else
 1786                 client->weapon_thunk = false;
 1787 
 1788         if (ent->deadflag)
 1789         {
 1790                 // wait for any button just going down
 1791                 if ( level.time > client->respawn_time)
 1792                 {
 1793                         // in deathmatch, only wait for attack button
 1794                         if (deathmatch->value)
 1795                                 buttonMask = BUTTON_ATTACK;
 1796                         else
 1797                                 buttonMask = -1;
 1798 
 1799                         if ( ( client->latched_buttons & buttonMask ) ||
 1800                                 (deathmatch->value && ((int)dmflags->value & DF_FORCE_RESPAWN) ) )
 1801                         {
 1802                                 respawn(ent);
 1803                                 client->latched_buttons = 0;
 1804                         }
 1805                 }
 1806                 return;
 1807         }
 1808 
 1809         // add player trail so monsters can follow
 1810         if (!deathmatch->value)
 1811                 if (!visible (ent, PlayerTrail_LastSpot() ) )
 1812                         PlayerTrail_Add (ent->s.old_origin);
 1813 
 1814         client->latched_buttons = 0;
 1815 }
 1816