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