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(edict_t * ent)171 void SP_info_player_intermission(edict_t *ent)
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.0f;
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 score on respawn
1060 	ent->client->pers.score = ent->client->resp.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 = 0;
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 		client->ps.pmove.delta_angles[i] = ANGLE2SHORT(spawn_angles[i] - client->resp.cmd_angles[i]);
1225 
1226 	ent->s.angles[PITCH] = 0;
1227 	ent->s.angles[YAW] = spawn_angles[YAW];
1228 	ent->s.angles[ROLL] = 0;
1229 	VectorCopy (ent->s.angles, client->ps.viewangles);
1230 	VectorCopy (ent->s.angles, client->v_angle);
1231 
1232 	// spawn a spectator
1233 	if (client->pers.spectator) {
1234 		client->chase_target = NULL;
1235 
1236 		client->resp.spectator = true;
1237 
1238 		ent->movetype = MOVETYPE_NOCLIP;
1239 		ent->solid = SOLID_NOT;
1240 		ent->svflags |= SVF_NOCLIENT;
1241 		ent->client->ps.gunindex = 0;
1242 		gi.linkentity (ent);
1243 		return;
1244 	} else
1245 		client->resp.spectator = false;
1246 
1247 	if (!KillBox (ent))
1248 	{	// could't spawn in?
1249 	}
1250 
1251 	gi.linkentity (ent);
1252 
1253 	// force the current weapon up
1254 	client->newweapon = client->pers.weapon;
1255 	ChangeWeapon (ent);
1256 }
1257 
1258 /*
1259 =====================
1260 ClientBeginDeathmatch
1261 
1262 A client has just connected to the server in
1263 deathmatch mode, so clear everything out before starting them.
1264 =====================
1265 */
ClientBeginDeathmatch(edict_t * ent)1266 void ClientBeginDeathmatch (edict_t *ent)
1267 {
1268 	G_InitEdict (ent);
1269 
1270 	InitClientResp (ent->client);
1271 
1272 	// locate ent at a spawn point
1273 	PutClientInServer (ent);
1274 
1275 	// send effect
1276 	gi.WriteByte (svc_muzzleflash);
1277 	gi.WriteShort (ent-g_edicts);
1278 	gi.WriteByte (MZ_LOGIN);
1279 	gi.multicast (ent->s.origin, MULTICAST_PVS);
1280 
1281 	gi.bprintf (PRINT_HIGH, "%s entered the game\n", ent->client->pers.netname);
1282 
1283 	// make sure all view stuff is valid
1284 	ClientEndServerFrame (ent);
1285 }
1286 
1287 
1288 /*
1289 ===========
1290 ClientBegin
1291 
1292 called when a client has finished connecting, and is ready
1293 to be placed into the game.  This will happen every level load.
1294 ============
1295 */
ClientBegin(edict_t * ent)1296 void ClientBegin (edict_t *ent)
1297 {
1298 	int		i;
1299 
1300 	ent->client = game.clients + (ent - g_edicts - 1);
1301 
1302 	if (deathmatch->value)
1303 	{
1304 		ClientBeginDeathmatch (ent);
1305 		return;
1306 	}
1307 
1308 	// if there is already a body waiting for us (a loadgame), just
1309 	// take it, otherwise spawn one from scratch
1310 	if (ent->inuse == true)
1311 	{
1312 		// the client has cleared the client side viewangles upon
1313 		// connecting to the server, which is different than the
1314 		// state when the game is saved, so we need to compensate
1315 		// with deltaangles
1316 		for (i=0 ; i<3 ; i++)
1317 			ent->client->ps.pmove.delta_angles[i] = ANGLE2SHORT(ent->client->ps.viewangles[i]);
1318 	}
1319 	else
1320 	{
1321 		// a spawn point will completely reinitialize the entity
1322 		// except for the persistant data that was initialized at
1323 		// ClientConnect() time
1324 		G_InitEdict (ent);
1325 		ent->classname = "player";
1326 		InitClientResp (ent->client);
1327 		PutClientInServer (ent);
1328 	}
1329 
1330 	if (level.intermissiontime)
1331 	{
1332 		MoveClientToIntermission (ent);
1333 	}
1334 	else
1335 	{
1336 		// send effect if in a multiplayer game
1337 		if (game.maxclients > 1)
1338 		{
1339 			gi.WriteByte (svc_muzzleflash);
1340 			gi.WriteShort (ent-g_edicts);
1341 			gi.WriteByte (MZ_LOGIN);
1342 			gi.multicast (ent->s.origin, MULTICAST_PVS);
1343 
1344 			gi.bprintf (PRINT_HIGH, "%s entered the game\n", ent->client->pers.netname);
1345 		}
1346 	}
1347 
1348 	// make sure all view stuff is valid
1349 	ClientEndServerFrame (ent);
1350 }
1351 
1352 /*
1353 ===========
1354 ClientUserInfoChanged
1355 
1356 called whenever the player updates a userinfo variable.
1357 
1358 The game can override any of the settings in place
1359 (forcing skins or names, etc) before copying it off.
1360 ============
1361 */
ClientUserinfoChanged(edict_t * ent,char * userinfo)1362 void ClientUserinfoChanged (edict_t *ent, char *userinfo)
1363 {
1364 	char	*s;
1365 	int		playernum;
1366 
1367 	// check for malformed or illegal info strings
1368 	if (!Info_Validate(userinfo))
1369 	{
1370 		strcpy (userinfo, "\\name\\badinfo\\skin\\male/grunt");
1371 	}
1372 
1373 	// set name
1374 	s = Info_ValueForKey (userinfo, "name");
1375 	strncpy (ent->client->pers.netname, s, sizeof(ent->client->pers.netname)-1);
1376 
1377 	// set spectator
1378 	s = Info_ValueForKey (userinfo, "spectator");
1379 	// spectators are only supported in deathmatch
1380 	if (deathmatch->value && *s && strcmp(s, "0"))
1381 		ent->client->pers.spectator = true;
1382 	else
1383 		ent->client->pers.spectator = false;
1384 
1385 	// set skin
1386 	s = Info_ValueForKey (userinfo, "skin");
1387 
1388 	playernum = ent-g_edicts-1;
1389 
1390 	// combine name and skin into a configstring
1391 	gi.configstring (CS_PLAYERSKINS+playernum, va("%s\\%s", ent->client->pers.netname, s) );
1392 
1393 	// fov
1394 	if (deathmatch->value && ((int)dmflags->value & DF_FIXED_FOV))
1395 	{
1396 		ent->client->ps.fov = 90;
1397 	}
1398 	else
1399 	{
1400 		ent->client->ps.fov = atoi(Info_ValueForKey(userinfo, "fov"));
1401 		if (ent->client->ps.fov < 1)
1402 			ent->client->ps.fov = 90;
1403 		else if (ent->client->ps.fov > 160)
1404 			ent->client->ps.fov = 160;
1405 	}
1406 
1407 	// handedness
1408 	s = Info_ValueForKey (userinfo, "hand");
1409 	if (strlen(s))
1410 	{
1411 		ent->client->pers.hand = atoi(s);
1412 	}
1413 
1414 	// save off the userinfo in case we want to check something later
1415 	strncpy (ent->client->pers.userinfo, userinfo, sizeof(ent->client->pers.userinfo)-1);
1416 }
1417 
1418 
1419 /*
1420 ===========
1421 ClientConnect
1422 
1423 Called when a player begins connecting to the server.
1424 The game can refuse entrance to a client by returning false.
1425 If the client is allowed, the connection process will continue
1426 and eventually get to ClientBegin()
1427 Changing levels will NOT cause this to be called again, but
1428 loadgames will.
1429 ============
1430 */
ClientConnect(edict_t * ent,char * userinfo)1431 qboolean ClientConnect (edict_t *ent, char *userinfo)
1432 {
1433 	char	*value;
1434 
1435 	// check to see if they are on the banned IP list
1436 	value = Info_ValueForKey (userinfo, "ip");
1437 	if (SV_FilterPacket(value)) {
1438 		Info_SetValueForKey(userinfo, "rejmsg", "Banned.");
1439 		return false;
1440 	}
1441 
1442 	// check for a spectator
1443 	value = Info_ValueForKey (userinfo, "spectator");
1444 	if (deathmatch->value && *value && strcmp(value, "0")) {
1445 		int i, numspec;
1446 
1447 		if (*spectator_password->string &&
1448 			strcmp(spectator_password->string, "none") &&
1449 			strcmp(spectator_password->string, value)) {
1450 			Info_SetValueForKey(userinfo, "rejmsg", "Spectator password required or incorrect.");
1451 			return false;
1452 		}
1453 
1454 		// count spectators
1455 		for (i = numspec = 0; i < maxclients->value; i++)
1456 			if (g_edicts[i+1].inuse && g_edicts[i+1].client->pers.spectator)
1457 				numspec++;
1458 
1459 		if (numspec >= maxspectators->value) {
1460 			Info_SetValueForKey(userinfo, "rejmsg", "Server spectator limit is full.");
1461 			return false;
1462 		}
1463 	} else {
1464 		// check for a password
1465 		value = Info_ValueForKey (userinfo, "password");
1466 		if (*password->string && strcmp(password->string, "none") &&
1467 			strcmp(password->string, value)) {
1468 			Info_SetValueForKey(userinfo, "rejmsg", "Password required or incorrect.");
1469 			return false;
1470 		}
1471 	}
1472 
1473 
1474 	// they can connect
1475 	ent->client = game.clients + (ent - g_edicts - 1);
1476 
1477 	// if there is already a body waiting for us (a loadgame), just
1478 	// take it, otherwise spawn one from scratch
1479 	if (ent->inuse == false)
1480 	{
1481 		// clear the respawning variables
1482 		InitClientResp (ent->client);
1483 		if (!game.autosaved || !ent->client->pers.weapon)
1484 			InitClientPersistant (ent->client);
1485 	}
1486 
1487 	ClientUserinfoChanged (ent, userinfo);
1488 
1489 	if (game.maxclients > 1)
1490 		gi.dprintf ("%s connected\n", ent->client->pers.netname);
1491 
1492 	ent->client->pers.connected = true;
1493 	return true;
1494 }
1495 
1496 /*
1497 ===========
1498 ClientDisconnect
1499 
1500 Called when a player drops from the server.
1501 Will not be called between levels.
1502 ============
1503 */
ClientDisconnect(edict_t * ent)1504 void ClientDisconnect (edict_t *ent)
1505 {
1506 	int		playernum;
1507 
1508 	if (!ent->client)
1509 		return;
1510 
1511 	gi.bprintf (PRINT_HIGH, "%s disconnected\n", ent->client->pers.netname);
1512 
1513 	// send effect
1514 	gi.WriteByte (svc_muzzleflash);
1515 	gi.WriteShort (ent-g_edicts);
1516 	gi.WriteByte (MZ_LOGOUT);
1517 	gi.multicast (ent->s.origin, MULTICAST_PVS);
1518 
1519 	gi.unlinkentity (ent);
1520 	ent->s.modelindex = 0;
1521 	ent->solid = SOLID_NOT;
1522 	ent->inuse = false;
1523 	ent->classname = "disconnected";
1524 	ent->client->pers.connected = false;
1525 
1526 	playernum = ent-g_edicts-1;
1527 	gi.configstring (CS_PLAYERSKINS+playernum, "");
1528 }
1529 
1530 
1531 //==============================================================
1532 
1533 
1534 edict_t	*pm_passent;
1535 
1536 // pmove doesn't need to know about passent and contentmask
PM_trace(vec3_t start,vec3_t mins,vec3_t maxs,vec3_t end)1537 trace_t	PM_trace (vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end)
1538 {
1539 	if (pm_passent->health > 0)
1540 		return gi.trace (start, mins, maxs, end, pm_passent, MASK_PLAYERSOLID);
1541 	else
1542 		return gi.trace (start, mins, maxs, end, pm_passent, MASK_DEADSOLID);
1543 }
1544 
CheckBlock(void * b,int c)1545 unsigned CheckBlock (void *b, int c)
1546 {
1547 	int	v,i;
1548 	v = 0;
1549 	for (i=0 ; i<c ; i++)
1550 		v+= ((byte *)b)[i];
1551 	return v;
1552 }
PrintPmove(pmove_t * pm)1553 void PrintPmove (pmove_t *pm)
1554 {
1555 	unsigned	c1, c2;
1556 
1557 	c1 = CheckBlock (&pm->s, sizeof(pm->s));
1558 	c2 = CheckBlock (&pm->cmd, sizeof(pm->cmd));
1559 	gi.dprintf ("sv %3i:%i %i\n", pm->cmd.impulse, c1, c2);
1560 }
1561 
1562 /*
1563 ==============
1564 ClientThink
1565 
1566 This will be called once for each client frame, which will
1567 usually be a couple times for each server frame.
1568 ==============
1569 */
ClientThink(edict_t * ent,usercmd_t * ucmd)1570 void ClientThink (edict_t *ent, usercmd_t *ucmd)
1571 {
1572 	gclient_t	*client;
1573 	edict_t	*other;
1574 	int		i, j;
1575 	pmove_t	pm;
1576 
1577 	level.current_entity = ent;
1578 	client = ent->client;
1579 
1580 	if (level.intermissiontime)
1581 	{
1582 		client->ps.pmove.pm_type = PM_FREEZE;
1583 		// can exit intermission after five seconds
1584 		if (level.time > level.intermissiontime + 5.0f
1585 			&& (ucmd->buttons & BUTTON_ANY) )
1586 			level.exitintermission = true;
1587 		return;
1588 	}
1589 
1590 	pm_passent = ent;
1591 
1592 	if (ent->client->chase_target) {
1593 
1594 		client->resp.cmd_angles[0] = SHORT2ANGLE(ucmd->angles[0]);
1595 		client->resp.cmd_angles[1] = SHORT2ANGLE(ucmd->angles[1]);
1596 		client->resp.cmd_angles[2] = SHORT2ANGLE(ucmd->angles[2]);
1597 
1598 	} else {
1599 
1600 		// set up for pmove
1601 		memset (&pm, 0, sizeof(pm));
1602 
1603 		if (ent->movetype == MOVETYPE_NOCLIP)
1604 			client->ps.pmove.pm_type = PM_SPECTATOR;
1605 		else if (ent->s.modelindex != 255)
1606 			client->ps.pmove.pm_type = PM_GIB;
1607 		else if (ent->deadflag)
1608 			client->ps.pmove.pm_type = PM_DEAD;
1609 		else
1610 			client->ps.pmove.pm_type = PM_NORMAL;
1611 
1612 		client->ps.pmove.gravity = sv_gravity->value;
1613 		pm.s = client->ps.pmove;
1614 
1615 		for (i=0 ; i<3 ; i++)
1616 		{
1617 			pm.s.origin[i] = ent->s.origin[i]*8;
1618 			pm.s.velocity[i] = ent->velocity[i]*8;
1619 		}
1620 
1621 		if (memcmp(&client->old_pmove, &pm.s, sizeof(pm.s)))
1622 		{
1623 			pm.snapinitial = true;
1624 	//		gi.dprintf ("pmove changed!\n");
1625 		}
1626 
1627 		pm.cmd = *ucmd;
1628 
1629 		pm.trace = PM_trace;	// adds default parms
1630 		pm.pointcontents = gi.pointcontents;
1631 
1632 		// perform a pmove
1633 		gi.Pmove (&pm);
1634 
1635 		// save results of pmove
1636 		client->ps.pmove = pm.s;
1637 		client->old_pmove = pm.s;
1638 
1639 		for (i=0 ; i<3 ; i++)
1640 		{
1641 			ent->s.origin[i] = pm.s.origin[i]*0.125;
1642 			ent->velocity[i] = pm.s.velocity[i]*0.125;
1643 		}
1644 
1645 		VectorCopy (pm.mins, ent->mins);
1646 		VectorCopy (pm.maxs, ent->maxs);
1647 
1648 		client->resp.cmd_angles[0] = SHORT2ANGLE(ucmd->angles[0]);
1649 		client->resp.cmd_angles[1] = SHORT2ANGLE(ucmd->angles[1]);
1650 		client->resp.cmd_angles[2] = SHORT2ANGLE(ucmd->angles[2]);
1651 
1652 		if (ent->groundentity && !pm.groundentity && (pm.cmd.upmove >= 10) && (pm.waterlevel == 0))
1653 		{
1654 			gi.sound(ent, CHAN_VOICE, gi.soundindex("*jump1.wav"), 1, ATTN_NORM, 0);
1655 			PlayerNoise(ent, ent->s.origin, PNOISE_SELF);
1656 		}
1657 
1658 		ent->viewheight = pm.viewheight;
1659 		ent->waterlevel = pm.waterlevel;
1660 		ent->watertype = pm.watertype;
1661 		ent->groundentity = pm.groundentity;
1662 		if (pm.groundentity)
1663 			ent->groundentity_linkcount = pm.groundentity->linkcount;
1664 
1665 		if (ent->deadflag)
1666 		{
1667 			client->ps.viewangles[ROLL] = 40;
1668 			client->ps.viewangles[PITCH] = -15;
1669 			client->ps.viewangles[YAW] = client->killer_yaw;
1670 		}
1671 		else
1672 		{
1673 			VectorCopy (pm.viewangles, client->v_angle);
1674 			VectorCopy (pm.viewangles, client->ps.viewangles);
1675 		}
1676 
1677 		gi.linkentity (ent);
1678 
1679 		if (ent->movetype != MOVETYPE_NOCLIP)
1680 			G_TouchTriggers (ent);
1681 
1682 		// touch other objects
1683 		for (i=0 ; i<pm.numtouch ; i++)
1684 		{
1685 			other = pm.touchents[i];
1686 			for (j=0 ; j<i ; j++)
1687 				if (pm.touchents[j] == other)
1688 					break;
1689 			if (j != i)
1690 				continue;	// duplicated
1691 			if (!other->touch)
1692 				continue;
1693 			other->touch (other, ent, NULL, NULL);
1694 		}
1695 
1696 	}
1697 
1698 	client->oldbuttons = client->buttons;
1699 	client->buttons = ucmd->buttons;
1700 	client->latched_buttons |= client->buttons & ~client->oldbuttons;
1701 
1702 	// save light level the player is standing on for
1703 	// monster sighting AI
1704 	ent->light_level = ucmd->lightlevel;
1705 
1706 	// fire weapon from final position if needed
1707 	if (client->latched_buttons & BUTTON_ATTACK)
1708 	{
1709 		if (client->resp.spectator) {
1710 
1711 			client->latched_buttons = 0;
1712 
1713 			if (client->chase_target) {
1714 				client->chase_target = NULL;
1715 				client->ps.pmove.pm_flags &= ~PMF_NO_PREDICTION;
1716 			} else
1717 				GetChaseTarget(ent);
1718 
1719 		} else if (!client->weapon_thunk) {
1720 			client->weapon_thunk = true;
1721 			Think_Weapon (ent);
1722 		}
1723 	}
1724 
1725 	if (client->resp.spectator) {
1726 		if (ucmd->upmove >= 10) {
1727 			if (!(client->ps.pmove.pm_flags & PMF_JUMP_HELD)) {
1728 				client->ps.pmove.pm_flags |= PMF_JUMP_HELD;
1729 				if (client->chase_target)
1730 					ChaseNext(ent);
1731 				else
1732 					GetChaseTarget(ent);
1733 			}
1734 		} else
1735 			client->ps.pmove.pm_flags &= ~PMF_JUMP_HELD;
1736 	}
1737 
1738 	// update chase cam if being followed
1739 	for (i = 1; i <= maxclients->value; i++) {
1740 		other = g_edicts + i;
1741 		if (other->inuse && other->client->chase_target == ent)
1742 			UpdateChaseCam(other);
1743 	}
1744 }
1745 
1746 
1747 /*
1748 ==============
1749 ClientBeginServerFrame
1750 
1751 This will be called once for each server frame, before running
1752 any other entities in the world.
1753 ==============
1754 */
ClientBeginServerFrame(edict_t * ent)1755 void ClientBeginServerFrame (edict_t *ent)
1756 {
1757 	gclient_t	*client;
1758 	int			buttonMask;
1759 
1760 	if (level.intermissiontime)
1761 		return;
1762 
1763 	client = ent->client;
1764 
1765 	if (deathmatch->value &&
1766 		client->pers.spectator != client->resp.spectator &&
1767 		(level.time - client->respawn_time) >= 5) {
1768 		spectator_respawn(ent);
1769 		return;
1770 	}
1771 
1772 	// run weapon animations if it hasn't been done by a ucmd_t
1773 	if (!client->weapon_thunk && !client->resp.spectator)
1774 		Think_Weapon (ent);
1775 	else
1776 		client->weapon_thunk = false;
1777 
1778 	if (ent->deadflag)
1779 	{
1780 		// wait for any button just going down
1781 		if ( level.time > client->respawn_time)
1782 		{
1783 			// in deathmatch, only wait for attack button
1784 			if (deathmatch->value)
1785 				buttonMask = BUTTON_ATTACK;
1786 			else
1787 				buttonMask = -1;
1788 
1789 			if ( ( client->latched_buttons & buttonMask ) ||
1790 				(deathmatch->value && ((int)dmflags->value & DF_FORCE_RESPAWN) ) )
1791 			{
1792 				respawn(ent);
1793 				client->latched_buttons = 0;
1794 			}
1795 		}
1796 		return;
1797 	}
1798 
1799 	// add player trail so monsters can follow
1800 	if (!deathmatch->value)
1801 		if (!visible (ent, PlayerTrail_LastSpot() ) )
1802 			PlayerTrail_Add (ent->s.old_origin);
1803 
1804 	client->latched_buttons = 0;
1805 }
1806