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 
SP_FixCoopSpots(edict_t * self)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 
SP_CreateCoopSpots(edict_t * self)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 */
SP_info_player_start(edict_t * self)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 */
SP_info_player_deathmatch(edict_t * self)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 
SP_info_player_coop(edict_t * self)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 */
SP_info_player_intermission(void)171 void SP_info_player_intermission(void)
172 {
173 }
174 
175 
176 //=======================================================================
177 
178 
player_pain(edict_t * self,edict_t * other,float kick,int damage)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 
IsFemale(edict_t * ent)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 
IsNeutral(edict_t * ent)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 
ClientObituary(edict_t * self,edict_t * inflictor,edict_t * attacker)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 
TossClientWeapon(edict_t * self)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 */
LookAtKiller(edict_t * self,edict_t * inflictor,edict_t * attacker)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 */
player_die(edict_t * self,edict_t * inflictor,edict_t * attacker,int damage,vec3_t point)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 */
InitClientPersistant(gclient_t * client)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 
InitClientResp(gclient_t * client)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 */
SaveClientData(void)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 
FetchClientEntData(edict_t * ent)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 */
PlayersRangeFromSpot(edict_t * spot)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 */
SelectRandomDeathmatchSpawnPoint(void)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 */
SelectFarthestDeathmatchSpawnPoint(void)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 
SelectDeathmatchSpawnPoint(void)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 
SelectCoopSpawnPoint(edict_t * ent)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 */
SelectSpawnPoint(edict_t * ent,vec3_t origin,vec3_t angles)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 
InitBodyQue(void)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 
body_die(edict_t * self,edict_t * inflictor,edict_t * attacker,int damage,vec3_t point)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 
CopyToBodyQue(edict_t * ent)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 
respawn(edict_t * self)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  */
spectator_respawn(edict_t * ent)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 */
PutClientInServer(edict_t * ent)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 */
ClientBeginDeathmatch(edict_t * ent)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 */
ClientBegin(edict_t * ent)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 */
ClientUserinfoChanged(edict_t * ent,char * userinfo)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 */
ClientConnect(edict_t * ent,char * userinfo)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 */
ClientDisconnect(edict_t * ent)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
PM_trace(vec3_t start,vec3_t mins,vec3_t maxs,vec3_t end)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 
CheckBlock(void * b,int c)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 }
PrintPmove(pmove_t * pm)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 */
ClientThink(edict_t * ent,usercmd_t * ucmd)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 */
ClientBeginServerFrame(edict_t * ent)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