Quake-2

Quake 2 GPL Source Release
Log | Files | Refs

p_client.c (38882B)


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