1 /*
2 Copyright (C) 1997-2001 Id Software, Inc.
3 Copyright (C) 2010 COR Entertainment, LLC.
4 
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License
7 as published by the Free Software Foundation; either version 2
8 of the License, or (at your option) any later version.
9 
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 
14 See the GNU General Public License for more details.
15 
16 You should have received a copy of the GNU General Public License along
17 with this program; if not, write to the Free Software Foundation, Inc.,
18 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 */
20 
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24 
25 #include "g_local.h"
26 #include "m_player.h"
27 
28 /* Number of gibs to throw on death with lots of damage (including Client Head, where applicable) */
29 #define DEATH_GIBS_TO_THROW 5
30 void ClientUserinfoChanged (edict_t *ent, char *userinfo, int whereFrom);
31 void ClientDisconnect (edict_t *ent);
32 void SP_misc_teleporter_dest (edict_t *ent);
33 
34 /*QUAKED info_player_start (1 0 0) (-16 -16 -24) (16 16 32)
35 The normal starting point for a level.
36 */
SP_info_player_start(edict_t * self)37 void SP_info_player_start(edict_t *self)
38 {
39 	return;
40 }
41 
42 /*QUAKED info_player_deathmatch (1 0 1) (-16 -16 -24) (16 16 32)
43 potential spawning position for deathmatch games
44 */
SP_info_player_deathmatch(edict_t * self)45 void SP_info_player_deathmatch(edict_t *self)
46 {
47 	if (!deathmatch->value)
48 	{
49 		G_FreeEdict (self);
50 		return;
51 	}
52 	//SP_misc_teleporter_dest (self);
53 }
SP_info_player_red(edict_t * self)54 void SP_info_player_red(edict_t *self)
55 {
56 	if (!deathmatch->value)
57 	{
58 		G_FreeEdict (self);
59 		return;
60 	}
61 	//SP_misc_teleporter_dest (self);
62 }
SP_info_player_blue(edict_t * self)63 void SP_info_player_blue(edict_t *self)
64 {
65 	if (!deathmatch->value)
66 	{
67 		G_FreeEdict (self);
68 		return;
69 	}
70 	//SP_misc_teleporter_dest (self);
71 }
72 
73 /*QUAKED info_player_intermission (1 0 1) (-16 -16 -24) (16 16 32)
74 The deathmatch intermission point will be at one of these
75 Use 'angles' instead of 'angle', so you can set pitch or roll as well as yaw.  'pitch yaw roll'
76 */
SP_info_player_intermission(edict_t * ent)77 void SP_info_player_intermission(edict_t *ent)
78 {
79 }
80 
81 
82 //=======================================================================
83 
84 
player_pain(edict_t * self,edict_t * other,float kick,int damage)85 void player_pain (edict_t *self, edict_t *other, float kick, int damage)
86 {
87 	// player pain is handled at the end of the frame in P_DamageFeedback
88 	if(self->is_bot)
89 		self->oldenemy = other;
90 }
91 
92 
IsFemale(edict_t * ent)93 qboolean IsFemale (edict_t *ent)
94 {
95 	char		*info;
96 
97 	if (!ent->client)
98 		return false;
99 
100 	info = Info_ValueForKey (ent->client->pers.userinfo, "skin");
101 	if (info[0] == 'f' || info[0] == 'F')
102 		return true;
103 	return false;
104 }
105 
106 
ClientObituary(edict_t * self,edict_t * inflictor,edict_t * attacker)107 void ClientObituary (edict_t *self, edict_t *inflictor, edict_t *attacker)
108 {
109 	int		mod=0, msg;
110 	char		*message;
111 	char		*message2;
112 	qboolean	ff;
113 	char		*chatmsg;
114 	char		*tauntmsg;
115 	char cleanname[PLAYERNAME_SIZE];
116 	char cleanname2[PLAYERNAME_SIZE];
117 	int			i, pos, total, place;
118 	edict_t		*cl_ent;
119 	gitem_t		*it;
120 
121 	if (deathmatch->value)
122 	{
123 		ff = meansOfDeath & MOD_FRIENDLY_FIRE;
124 		mod = meansOfDeath & ~MOD_FRIENDLY_FIRE;
125 		message = NULL;
126 		chatmsg = NULL;
127 		tauntmsg = NULL;
128 		message2 = "";
129 
130 		switch (mod)
131 		{
132 		case MOD_SUICIDE:
133 			message = "suicides";
134 			break;
135 		case MOD_FALLING:
136 			message = "cratered";
137 			break;
138 		case MOD_CRUSH:
139 			message = "was squished";
140 			break;
141 		case MOD_WATER:
142 			message = "sank like a rock";
143 			break;
144 		case MOD_SLIME:
145 			message = "melted";
146 			break;
147 		case MOD_LAVA:
148 			message = "does a back flip into the lava";
149 			break;
150 		case MOD_EXPLOSIVE:
151 		case MOD_BARREL:
152 			message = "blew up";
153 			break;
154 		case MOD_EXIT:
155 			message = "found a way out";
156 			break;
157 		case MOD_TARGET_LASER:
158 			message = "saw the light";
159 			break;
160 		case MOD_TARGET_BLASTER:
161 			message = "got blasted";
162 			break;
163 		case MOD_BOMB:
164 		case MOD_SPLASH:
165 		case MOD_TRIGGER_HURT:
166 			message = "was in the wrong place";
167 			break;
168 		}
169 		if (attacker == self)
170 		{
171 			switch (mod)
172 			{
173 			case MOD_CAMPING:
174 				message = "was killed for camping";
175 				break;
176 			case MOD_PLASMA_SPLASH:
177 				if (IsFemale(self))
178 					message = "melted herself";
179 				else
180 					message = "melted himself";
181 				break;
182 			case MOD_R_SPLASH:
183 				if (IsFemale(self))
184 					message = "blew herself up";
185 				else
186 					message = "blew himself up";
187 				break;
188 			case MOD_VAPORIZER:
189 				message = "should have used a smaller gun";
190 				break;
191 			default:
192 				if (IsFemale(self))
193 					message = "killed herself";
194 				else
195 					message = "killed himself";
196 				break;
197 			}
198 		}
199 		if (message)
200 		{
201 			safe_bprintf (PRINT_MEDIUM, "%s %s.\n", self->client->pers.netname, message);
202 			if (deathmatch->value) {
203 				self->client->resp.score--;
204 				self->client->resp.deaths++;
205 			}
206 			self->enemy = NULL;
207 			self->client->kill_streak = 0; //reset, you are dead
208 			return;
209 		}
210 
211 		self->enemy = attacker;
212 		if (attacker && attacker->client)
213 		{
214 			//clean up names, get rid of escape chars
215 			G_CleanPlayerName ( self->client->pers.netname , cleanname );
216 			G_CleanPlayerName ( attacker->client->pers.netname , cleanname2 );
217 
218 			if(!attacker->is_bot) {
219 				pos = 0;
220 				total = 0;
221 				for (i=0 ; i<game.maxclients ; i++)
222 				{
223 					cl_ent = g_edicts + 1 + i;
224 					if (!cl_ent->inuse || game.clients[i].resp.spectator)
225 						continue;
226 
227 					if(attacker->client->resp.score+1 >= game.clients[i].resp.score)
228 						pos++;
229 
230 					total++;
231 				}
232 				place = total - pos;
233 				if(place < 3) {
234 					switch(place) {
235 					case 0:
236 						safe_centerprintf(attacker, "You fragged %s\n1st place with %i frags\n", cleanname, attacker->client->resp.score+1);
237 						break;
238 					case 1:
239 						safe_centerprintf(attacker, "You fragged %s\n2nd place with %i frags\n", cleanname, attacker->client->resp.score+1);
240 						break;
241 					case 2:
242 						safe_centerprintf(attacker, "You fragged %s\n3rd place with %i frags\n", cleanname, attacker->client->resp.score+1);
243 						break;
244 					default:
245 						break;
246 					}
247 				}
248 				else
249 					safe_centerprintf(attacker, "You fragged %s\n", cleanname);
250 
251 			}
252 
253 			switch (mod)
254 			{
255 			case MOD_BLASTER:
256 				message = "was blasted by";
257 				break;
258 			case MOD_VIOLATOR:
259 				message = "was probed by";
260 				break;
261 			case MOD_CGALTFIRE:
262 				message = "was blown away by";
263 				message2 = "'s chaingun burst";
264 				break;
265 			case MOD_CHAINGUN:
266 				message = "was cut in half by";
267 				message2 = "'s chaingun";
268 				break;
269 			case MOD_FLAME:
270 				message = "was burned by";
271 				message2 = "'s napalm";
272 				break;
273 			case MOD_ROCKET:
274 				message = "ate";
275 				message2 = "'s rocket";
276 				break;
277 			case MOD_R_SPLASH:
278 				message = "almost dodged";
279 				message2 = "'s rocket";
280 				break;
281 			case MOD_BEAMGUN:
282 				message = "was melted by";
283 				message2 = "'s beamgun";
284 				break;
285 			case MOD_DISRUPTOR:
286 				message = "was disrupted by";
287 				break;
288 			case MOD_SMARTGUN:
289 				message = "saw the pretty lights from";
290 				message2 = "'s smartgun";
291 				break;
292 			case MOD_VAPORIZER:
293 				message = "was disintegrated by";
294 				message2 = "'s vaporizer blast";
295 				break;
296 			case MOD_VAPORALTFIRE:
297 				message = "couldn't hide from";
298 				message2 = "'s vaporizer";
299 				break;
300 			case MOD_MINDERASER:
301 				message = "had it's mind erased by";
302 				message2 = "'s alien seeker";
303 				break;
304 			case MOD_PLASMA_SPLASH: //blaster splash damage
305 				message = "was melted";
306 				message2 = "'s plasma";
307 				break;
308 			case MOD_TELEFRAG:
309 				message = "tried to invade";
310 				message2 = "'s personal space";
311 				break;
312 			case MOD_GRAPPLE:
313 				message = "was caught by";
314 				message2 = "'s grapple";
315 				break;
316 			case MOD_HEADSHOT:
317 				message = "had its head blown off by";
318 			}
319 			//here is where the bot chat features will be added.
320 			//default is on.  Setting to 1 turns it off.
321 
322 			if ( !(dmflags->integer & DF_BOTCHAT) && self->is_bot)
323 			{
324 				msg = random() * 9;
325 				switch(msg){
326 				case 1:
327 					chatmsg = self->chatmsg1;
328 					break;
329 				case 2:
330 					chatmsg = self->chatmsg2;
331 					break;
332 				case 3:
333 					chatmsg = self->chatmsg3;
334 					break;
335 				case 4:
336 					chatmsg = self->chatmsg4;
337 					break;
338 				case 5:
339 					chatmsg = self->chatmsg5;
340 					break;
341 				case 6:
342 					chatmsg = self->chatmsg6;
343 					break;
344 				case 7:
345 					chatmsg = self->chatmsg7;
346 					break;
347 				case 8:
348 					chatmsg = self->chatmsg8;
349 					break;
350 				default:
351 					chatmsg = "%s: Stop it %s, you punk!";
352 					break;
353 				}
354 				if(chatmsg) {
355 					safe_bprintf (PRINT_CHAT, chatmsg, self->client->pers.netname, attacker->client->pers.netname);
356 					safe_bprintf (PRINT_CHAT, "\n");
357 
358 					gi.WriteByte (svc_temp_entity);
359 					gi.WriteByte (TE_SAYICON);
360 					gi.WritePosition (self->s.origin);
361 					gi.multicast (self->s.origin, MULTICAST_PVS);
362 				}
363 			}
364 
365 			//bot taunts
366 			if(!(dmflags->integer & DF_BOTCHAT) && attacker->is_bot) {
367 
368 				if(!(attacker->client->ps.pmove.pm_flags & PMF_DUCKED)) {
369 					attacker->state = STATE_STAND;
370 					attacker->s.frame = FRAME_taunt01-1;
371 					attacker->client->anim_end = FRAME_taunt17;
372 
373 					//print a taunt, or send taunt sound
374 					msg = random() * 24;
375 					switch(msg){
376 					case 1:
377 						tauntmsg = "%s: You should have used a bigger gun %s.\n";
378 						break;
379 					case 2:
380 						tauntmsg = "%s: You fight like your mom %s.\n";
381 						break;
382 					case 3:
383 						tauntmsg = "%s: And stay down %s!\n";
384 						break;
385 					case 4:
386 						tauntmsg = "%s: %s = pwned!\n";
387 						break;
388 					case 5:
389 						tauntmsg = "%s: All too easy, %s, all too easy.\n";
390 						break;
391 					case 6:
392 						tauntmsg = "%s: Ack! %s Ack! Ack!\n";
393 						break;
394 					case 7:
395 						tauntmsg = "%s: What a loser you are %s!\n";
396 						break;
397 					case 8:
398 						tauntmsg = "%s: %s, could you BE any more dead?\n";
399 						break;
400 					case 9:
401 					case 10:
402 					case 11:
403 					case 12:
404 					case 13:
405 					case 14:
406 					case 15:
407 					case 16:
408 					case 17:
409 					case 18:
410 					case 19:
411 					case 20:
412 					case 21:
413 					case 22:
414 					case 23:
415 					case 24:
416 						Cmd_VoiceTaunt_f(attacker);
417 						break;
418 					default:
419 						tauntmsg = "%s: You are useless to me, %s\n";
420 						break;
421 					}
422 					if(tauntmsg) {
423 						safe_bprintf (PRINT_CHAT, tauntmsg, attacker->client->pers.netname, self->client->pers.netname);
424 						//send an effect to show that the bot is taunting
425 						gi.WriteByte (svc_temp_entity);
426 						gi.WriteByte (TE_SAYICON);
427 						gi.WritePosition (attacker->s.origin);
428 						gi.multicast (attacker->s.origin, MULTICAST_PVS);
429 					}
430 				}
431 			}
432 
433 			if (message)
434 			{
435 				safe_bprintf (PRINT_MEDIUM,"%s %s %s%s\n", self->client->pers.netname, message, attacker->client->pers.netname, message2);
436 
437 				if (deathmatch->value)
438 				{
439 					if(mod == MOD_MINDERASER)
440 					{
441 						self->client->resp.reward_pts = 0;
442 						self->client->resp.powered = false;
443 					}
444 
445 					if (ff)
446 					{
447 						attacker->client->resp.score--;
448 						attacker->client->resp.deaths++;
449 						if ((dmflags->integer & DF_SKINTEAMS) && !ctf->value) {
450 							if(attacker->dmteam == RED_TEAM)
451 								red_team_score--;
452 							else
453 								blue_team_score--;
454 						}
455 					}
456 					else
457 					{
458 						attacker->client->resp.score++;
459 
460 						if(!self->groundentity)
461 						{
462 							attacker->client->resp.reward_pts+=3;
463 							safe_centerprintf(attacker, "Midair shot!\n");
464 						}
465 						else
466 							attacker->client->resp.reward_pts++;
467 
468 						if(mod == MOD_HEADSHOT)
469 						{
470 							//3 more pts for a headshot
471 							attacker->client->resp.reward_pts+=3;
472 							safe_centerprintf(attacker, "HEADSHOT!\n");
473 							gi.sound(attacker, CHAN_AUTO, gi.soundindex("misc/headshot.wav"), 1, ATTN_STATIC, 0);
474 						}
475 
476 						//mutators
477 						if(vampire->value)
478 						{
479 							attacker->health+=20;
480 							if(attacker->health > attacker->max_health)
481 								attacker->health = attacker->max_health;
482 						}
483 						self->client->resp.deaths++;
484 
485 						if ((dmflags->integer & DF_SKINTEAMS)  && !ctf->value)
486 						{
487 							if(attacker->dmteam == RED_TEAM)
488 							{
489 								red_team_score++;
490 								safe_bprintf(PRINT_MEDIUM, "Red Team scores!\n");
491 								gi.sound (self, CHAN_AUTO, gi.soundindex("misc/red_scores.wav"), 1, ATTN_NONE, 0);
492 							}
493 							else
494 							{
495 								blue_team_score++;
496 								safe_bprintf(PRINT_MEDIUM, "Blue Team scores!\n");
497 								gi.sound (self, CHAN_AUTO, gi.soundindex("misc/blue_scores.wav"), 1, ATTN_NONE, 0);
498 
499 							}
500 						}
501 						//kill streaks
502 						attacker->client->kill_streak++;
503 						switch(attacker->client->kill_streak)
504 						{
505 							case 3:
506 								for (i=0 ; i<g_maxclients->value ; i++)
507 								{
508 									cl_ent = g_edicts + 1 + i;
509 									if (!cl_ent->inuse || cl_ent->is_bot)
510 										continue;
511 									safe_centerprintf(cl_ent, "%s is on a killing spree!\n", cleanname2);
512 								}
513 								break;
514 							case 5:
515 								for (i=0 ; i<g_maxclients->value ; i++)
516 								{
517 									cl_ent = g_edicts + 1 + i;
518 									if (!cl_ent->inuse || cl_ent->is_bot)
519 										continue;
520 									safe_centerprintf(cl_ent, "%s is on a rampage!\n", cleanname2);
521 								}
522 								gi.sound (self, CHAN_AUTO, gi.soundindex("misc/rampage.wav"), 1, ATTN_NONE, 0);
523 								attacker->client->resp.reward_pts+=10;
524 								break;
525 							case 8:
526 								for (i=0 ; i<g_maxclients->value ; i++)
527 								{
528 									cl_ent = g_edicts + 1 + i;
529 									if (!cl_ent->inuse || cl_ent->is_bot)
530 										continue;
531 									safe_centerprintf(cl_ent, "%s is unstoppable!\n", cleanname2);
532 								}
533 								break;
534 							case 10:
535 								for (i=0 ; i<g_maxclients->value ; i++)
536 								{
537 									cl_ent = g_edicts + 1 + i;
538 									if (!cl_ent->inuse || cl_ent->is_bot)
539 										continue;
540 									safe_centerprintf(cl_ent, "%s is a god!\n", cleanname2);
541 								}
542 								gi.sound (self, CHAN_AUTO, gi.soundindex("misc/godlike.wav"), 1, ATTN_NONE, 0);
543 								attacker->client->resp.reward_pts+=20;
544 								break;
545 							default:
546 								break;
547 						}
548 						if(self->client->kill_streak >=3)
549 						{
550 							for (i=0 ; i<g_maxclients->value ; i++)
551 							{
552 								cl_ent = g_edicts + 1 + i;
553 								if (!cl_ent->inuse || cl_ent->is_bot)
554 									continue;
555 								safe_centerprintf(cl_ent, "%s's killing spree\nended by %s!\n", cleanname, cleanname2);
556 							}
557 						}
558 					}
559 
560 				}
561 				if(attacker->client->resp.reward_pts >= g_reward->integer && !attacker->client->resp.powered)
562 				{
563 					//give them speed and invis powerups
564 					it = FindItem("Invisibility");
565 					attacker->client->pers.inventory[ITEM_INDEX(it)] += 1;
566 
567 					it = FindItem("Sproing");
568 					attacker->client->pers.inventory[ITEM_INDEX(it)] += 1;
569 
570 					it = FindItem("Haste");
571 					attacker->client->pers.inventory[ITEM_INDEX(it)] += 1;
572 
573 					attacker->client->resp.powered = true;
574 
575 					gi.sound (attacker, CHAN_AUTO, gi.soundindex("misc/pc_up.wav"), 1, ATTN_STATIC, 0);
576 				}
577 				self->client->kill_streak = 0; //reset, you are dead
578 				return;
579 			}
580 		}
581 
582 	}
583 
584 	if(mod == MOD_DEATHRAY)
585 	{
586 		safe_bprintf(PRINT_MEDIUM, "%s killed by Deathray!\n", self->client->pers.netname);
587 
588 		//immune player (activator) gets score increase
589 		for (i=0 ; i<g_maxclients->value ; i++)
590 		{
591 			cl_ent = g_edicts + 1 + i;
592 			if (!cl_ent->inuse || cl_ent->is_bot)
593 				continue;
594 			if(cl_ent->client)
595 				if(cl_ent->client->rayImmunity)
596 					cl_ent->client->resp.score++;
597 		}
598 		return;
599 	}
600 
601 	if(mod == MOD_SPIDER)
602 	{
603 		safe_bprintf(PRINT_MEDIUM, "%s killed by Spiderbot!\n", self->client->pers.netname);
604 
605 		self->client->resp.reward_pts = 0;
606 		self->client->resp.powered = false;
607 
608 		if(inflictor->owner->client)
609 			inflictor->owner->client->resp.score++;
610 		return;
611 	}
612 
613 	safe_bprintf (PRINT_MEDIUM,"%s died.\n", self->client->pers.netname);
614 	if (deathmatch->value)
615 	{
616 		self->client->resp.score--;
617 		self->client->resp.deaths++;
618 	}
619 
620 }
621 
622 
623 void Touch_Item (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf);
624 
TossClientWeapon(edict_t * self)625 void TossClientWeapon (edict_t *self)
626 {
627 	gitem_t		*item;
628 	edict_t		*drop;
629 	qboolean	quad;
630 	qboolean	sproing;
631 	qboolean	haste;
632 	float		spread;
633 
634 	if ((!deathmatch->value) || instagib->integer || rocket_arena->integer || insta_rockets->value)
635 	{
636 		return;
637 	}
638 
639 	item = self->client->pers.weapon;
640 	if (!self->client->pers.inventory[self->client->ammo_index] )
641 		item = NULL;
642 
643 #ifdef ALTERIA
644 	if (item && (strcmp (item->pickup_name, "Warriorpunch") == 0))
645 		item = NULL;
646 	if (item && (strcmp (item->pickup_name, "Wizardpunch") == 0))
647 		item = NULL;
648 #else
649 	if (item && (strcmp (item->pickup_name, "Blaster") == 0))
650 		item = NULL;
651 	if (item && (strcmp (item->pickup_name, "Alien Blaster") == 0))
652 		item = NULL;
653 	if (item && (strcmp (item->pickup_name, "Violator") == 0))
654 		item = NULL;
655 	if (item && (strcmp (item->pickup_name, "Minderaser") == 0))
656 		item = NULL;
657 	if (g_tactical->integer && item && (strcmp (item->pickup_name, "Alien Vaporizer") == 0))
658 		item = NULL;
659 #endif
660 
661 	if(g_tactical->integer)
662 	{
663 		//always drop your weapon on death, even if it isn't the current held item.
664 		if(self->client->pers.inventory[ITEM_INDEX(FindItem("Flame Thrower"))] == 1)
665 		{
666 			item = FindItem( "Flame Thrower" );
667 			if(item)
668 			{
669 				spread = 0;
670 				self->client->v_angle[YAW] -= spread;
671 				drop = Drop_Item (self, item);
672 				self->client->v_angle[YAW] += spread;
673 				drop->spawnflags = DROPPED_PLAYER_ITEM;
674 			}
675 		}
676 		if(self->client->pers.inventory[ITEM_INDEX(FindItem("Rocket Launcher"))] == 1)
677 		{
678 			item = FindItem( "Rocket Launcher" );
679 			if(item)
680 			{
681 				spread = 35;
682 				self->client->v_angle[YAW] -= spread;
683 				drop = Drop_Item (self, item);
684 				self->client->v_angle[YAW] += spread;
685 				drop->spawnflags = DROPPED_PLAYER_ITEM;
686 			}
687 		}
688 		if(self->client->pers.inventory[ITEM_INDEX(FindItem("Pulse Rifle"))] == 1)
689 		{
690 			item = FindItem( "Pulse Rifle" );
691 			if(item)
692 			{
693 				spread = 70;
694 				self->client->v_angle[YAW] -= spread;
695 				drop = Drop_Item (self, item);
696 				self->client->v_angle[YAW] += spread;
697 				drop->spawnflags = DROPPED_PLAYER_ITEM;
698 			}
699 		}
700 		if(self->client->pers.inventory[ITEM_INDEX(FindItem("Disruptor"))] == 1)
701 		{
702 			item = FindItem( "Disruptor" );
703 			if(item)
704 			{
705 				spread = 105;
706 				self->client->v_angle[YAW] -= spread;
707 				drop = Drop_Item (self, item);
708 				self->client->v_angle[YAW] += spread;
709 				drop->spawnflags = DROPPED_PLAYER_ITEM;
710 			}
711 		}
712 		if(self->client->pers.inventory[ITEM_INDEX(FindItem("Alien Disruptor"))] == 1)
713 		{
714 			item = FindItem( "Alien Disruptor" );
715 			if(item)
716 			{
717 				spread = 140;
718 				self->client->v_angle[YAW] -= spread;
719 				drop = Drop_Item (self, item);
720 				self->client->v_angle[YAW] += spread;
721 				drop->spawnflags = DROPPED_PLAYER_ITEM;
722 			}
723 		}
724 		if(self->client->pers.inventory[ITEM_INDEX(FindItem("Alien Smartgun"))] == 1)
725 		{
726 			item = FindItem( "Alien Smartgun" );
727 			if(item)
728 			{
729 				spread = 175;
730 				self->client->v_angle[YAW] -= spread;
731 				drop = Drop_Item (self, item);
732 				self->client->v_angle[YAW] += spread;
733 				drop->spawnflags = DROPPED_PLAYER_ITEM;
734 			}
735 		}
736 	}
737 	else
738 	{
739 		if (!(dmflags->integer & DF_QUAD_DROP))
740 			quad = false;
741 		else
742 			quad = (self->client->quad_framenum > (level.framenum + 10));
743 
744 		sproing = (self->client->sproing_framenum > (level.framenum + 10));
745 		haste = (self->client->haste_framenum > (level.framenum + 10));
746 
747 		if ((item && quad) || (item && haste) || (item && sproing))
748 			spread = 22.5;
749 		else
750 			spread = 0.0;
751 
752 		if (item)
753 		{
754 			self->client->v_angle[YAW] -= spread;
755 			drop = Drop_Item (self, item);
756 			self->client->v_angle[YAW] += spread;
757 			drop->spawnflags = DROPPED_PLAYER_ITEM;
758 		}
759 
760 		if (quad)
761 		{
762 			self->client->v_angle[YAW] += spread;
763 			drop = Drop_Item (self, FindItemByClassname ("item_quad"));
764 			self->client->v_angle[YAW] -= spread;
765 			drop->spawnflags |= DROPPED_PLAYER_ITEM;
766 
767 			drop->touch = Touch_Item;
768 			drop->nextthink = level.time + (self->client->quad_framenum - level.framenum) * FRAMETIME;
769 			drop->think = G_FreeEdict;
770 		}
771 		if (sproing && !self->client->resp.powered)
772 		{
773 			self->client->v_angle[YAW] += spread;
774 			drop = Drop_Item (self, FindItemByClassname ("item_sproing"));
775 			self->client->v_angle[YAW] -= spread;
776 			drop->spawnflags |= DROPPED_PLAYER_ITEM;
777 
778 			drop->touch = Touch_Item;
779 			drop->nextthink = level.time + (self->client->sproing_framenum - level.framenum) * FRAMETIME;
780 			drop->think = G_FreeEdict;
781 		}
782 		if (haste && !self->client->resp.powered)
783 		{
784 			self->client->v_angle[YAW] += spread;
785 			drop = Drop_Item (self, FindItemByClassname ("item_haste"));
786 			self->client->v_angle[YAW] -= spread;
787 			drop->spawnflags |= DROPPED_PLAYER_ITEM;
788 
789 			drop->touch = Touch_Item;
790 			drop->nextthink = level.time + (self->client->haste_framenum - level.framenum) * FRAMETIME;
791 			drop->think = G_FreeEdict;
792 		}
793 	}
794 }
795 
796 
797 /*
798 ==================
799 LookAtKiller
800 ==================
801 */
LookAtKiller(edict_t * self,edict_t * inflictor,edict_t * attacker)802 void LookAtKiller (edict_t *self, edict_t *inflictor, edict_t *attacker)
803 {
804 	vec3_t		dir;
805 
806 	if (attacker && attacker != world && attacker != self)
807 	{
808 		VectorSubtract (attacker->s.origin, self->s.origin, dir);
809 	}
810 	else if (inflictor && inflictor != world && inflictor != self)
811 	{
812 		VectorSubtract (inflictor->s.origin, self->s.origin, dir);
813 	}
814 	else
815 	{
816 		self->client->killer_yaw = self->s.angles[YAW];
817 		return;
818 	}
819 
820 	self->client->killer_yaw = 180/M_PI*atan2(dir[1], dir[0]);
821 }
822 
823 /*
824 ==================
825 player_die
826 ==================
827 */
player_die(edict_t * self,edict_t * inflictor,edict_t * attacker,int damage,vec3_t point)828 void player_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
829 {
830 	int	n;
831 	char	*info;
832 	int	got_vehicle = 0;
833 	int	number_of_gibs = 0;
834 	int	gib_effect = EF_GREENGIB;
835 	int hasFlag = false;
836 	gitem_t *it, *flag1_item, *flag2_item;
837 	int mod;
838 
839 	mod = meansOfDeath & ~MOD_FRIENDLY_FIRE;
840 
841 	if (self->in_vehicle) {
842 		Reset_player(self);	//get the player out of the vehicle
843 		Jet_Explosion(self); //blow that bitch up!
844 		got_vehicle = 1; //so we know how to handle dropping it
845 	}
846 
847 	VectorClear (self->avelocity);
848 
849 	self->takedamage = DAMAGE_YES;
850 	self->movetype = MOVETYPE_TOSS;
851 
852 	info = Info_ValueForKey (self->client->pers.userinfo, "skin");
853 
854 	self->s.modelindex2 = 0;	// remove linked weapon model
855 
856 	if(ctf->value)
857 		self->s.modelindex4 = 0;	// remove linked ctf flag
858 
859 	self->s.angles[0] = 0;
860 	self->s.angles[2] = 0;
861 
862 	self->s.sound = 0;
863 	self->client->weapon_sound = 0;
864 
865 	self->maxs[2] = -8;
866 
867 	self->svflags |= SVF_DEADMONSTER;
868 
869 	if (!self->deadflag)
870 	{
871 		self->client->respawn_time = level.time + 3.8;
872 
873 		//go into 3rd person view
874 		if (deathmatch->value)
875 			if(!self->is_bot)
876 				DeathcamStart(self);
877 
878 		self->client->ps.pmove.pm_type = PM_DEAD;
879 		ClientObituary (self, inflictor, attacker);
880 		if(got_vehicle) //special for vehicles
881 			VehicleDeadDrop(self);
882 		else
883 		{
884 			if(!excessive->value)
885 				TossClientWeapon (self);
886 		}
887 
888 		if(ctf->value)
889 		{
890 			//check to see if they had a flag
891 			flag1_item = flag2_item = NULL;
892 
893 			flag1_item = FindItemByClassname("item_flag_red");
894 			flag2_item = FindItemByClassname("item_flag_blue");
895 
896 			if (self->client->pers.inventory[ITEM_INDEX(flag1_item)] || self->client->pers.inventory[ITEM_INDEX(flag1_item)])
897 				hasFlag = true;
898 
899 			CTFDeadDropFlag(self, attacker);
900 			if(anticamp->value && meansOfDeath == MOD_SUICIDE && hasFlag)
901 			{
902 				//make campers really pay for hiding flags
903 				if(self->dmteam == BLUE_TEAM)
904 					CTFResetFlag(RED_TEAM);
905 				else
906 					CTFResetFlag(BLUE_TEAM);
907 			}
908 		}
909 		if(self->in_deathball)
910 			DeadDropDeathball(self);
911 
912 		CTFPlayerResetGrapple(self);
913 
914 		if (deathmatch->integer && !self->is_bot)
915 		{
916 			ACESP_UpdateBots();
917 			self->client->showscores = false; // override toggle
918 			Cmd_Score_f( self );
919 		}
920 
921 		if(attacker)
922 		{
923 			if(self->health < -40 && attacker->client)
924 			{
925 				attacker->client->resp.reward_pts++;
926 				if(attacker->client->resp.reward_pts >= g_reward->integer && !attacker->client->resp.powered)
927 				{	//give them speed and invis powerups
928 					it = FindItem("Invisibility");
929 					attacker->client->pers.inventory[ITEM_INDEX(it)] += 1;
930 
931 					it = FindItem("Sproing");
932 					attacker->client->pers.inventory[ITEM_INDEX(it)] += 1;
933 
934 					it = FindItem("Haste");
935 					attacker->client->pers.inventory[ITEM_INDEX(it)] += 1;
936 
937 					attacker->client->resp.powered = true;
938 
939 					gi.sound (attacker, CHAN_VOICE, gi.soundindex("misc/pc_up.wav"), 1, ATTN_STATIC, 0);
940 				}
941 			}
942 		}
943 	}
944 
945 	// remove powerups
946 	self->client->quad_framenum = 0;
947 	self->client->invincible_framenum = 0;
948 	self->client->haste_framenum = 0;
949 	self->client->sproing_framenum = 0;
950 	self->client->invis_framenum = 0;
951 
952 	// clear inventory
953 	memset(self->client->pers.inventory, 0, sizeof(self->client->pers.inventory));
954 
955 	if (self->health < -40)
956 	{	// gib
957 		self->takedamage	= DAMAGE_NO;
958 		self->s.modelindex3	= 0;    //remove helmet, if a martian
959 
960 		if(self->client->chasetoggle == 1)
961 		{
962 			/* If deathcam is active, switch client model to nothing */
963 			self->s.modelindex = 0;
964 			self->s.effects = 0;
965 			self->solid = SOLID_NOT;
966 
967 			number_of_gibs = DEATH_GIBS_TO_THROW;
968 		}
969 		else
970 		{
971 			/* No deathcam, handle player's view and model with ThrowClientHead() */
972 			ThrowClientHead (self, damage);
973 			number_of_gibs = DEATH_GIBS_TO_THROW - 1;
974 		}
975 
976 		if(self->ctype == 0)
977 		{	//alien
978 			gi.WriteByte (svc_temp_entity);
979 			gi.WriteByte (TE_DEATHFIELD);
980 			gi.WritePosition (self->s.origin);
981 			gi.multicast (self->s.origin, MULTICAST_PVS);
982 
983 			for (n= 0; n < number_of_gibs; n++) {
984 				if(mod == MOD_R_SPLASH || mod == MOD_ROCKET)
985 					ThrowGib (self, "models/objects/gibs/mart_gut/tris.md2", damage, GIB_METALLIC, EF_SHIPEXHAUST);
986 				else
987 					ThrowGib (self, "models/objects/gibs/mart_gut/tris.md2", damage, GIB_METALLIC, EF_GREENGIB);
988 				ThrowGib (self, "models/objects/debris2/tris.md2", damage, GIB_METALLIC, 0);
989 			}
990 		}
991 		else if(self->ctype == 2)
992 		{	//robot
993 			gib_effect = 0;
994 			for (n= 0; n < number_of_gibs; n++)
995 			{
996 				ThrowGib (self, "models/objects/debris3/tris.md2", damage, GIB_METALLIC, 0);
997 				ThrowGib (self, "models/objects/debris1/tris.md2", damage, GIB_METALLIC, 0);
998 			}
999 			//blow up too :)
1000 			gi.WriteByte (svc_temp_entity);
1001 			gi.WriteByte (TE_ROCKET_EXPLOSION);
1002 			gi.WritePosition (self->s.origin);
1003 			gi.multicast (self->s.origin, MULTICAST_PHS);
1004 		}
1005 		else
1006 		{	//human
1007 			gi.WriteByte (svc_temp_entity);
1008 			gi.WriteByte (TE_DEATHFIELD2);
1009 			gi.WritePosition (self->s.origin);
1010 			gi.WriteDir (self->s.angles);
1011 			gi.multicast (self->s.origin, MULTICAST_PVS);
1012 
1013 			gib_effect = EF_GIB;
1014 			for (n= 0; n < number_of_gibs; n++)
1015 			{
1016 				if(mod == MOD_R_SPLASH || mod == MOD_ROCKET)
1017 					ThrowGib (self, "models/objects/gibs/sm_meat/tris.md2", damage, GIB_METALLIC, EF_SHIPEXHAUST);
1018 				else
1019 					ThrowGib (self, "models/objects/gibs/sm_meat/tris.md2", damage, GIB_METALLIC, EF_GIB);
1020 			}
1021 		}
1022 
1023 		if(self->usegibs)
1024 		{
1025 			if(mod == MOD_R_SPLASH || mod == MOD_ROCKET)
1026 				gib_effect = EF_SHIPEXHAUST;
1027 			ThrowGib (self, self->head, damage, GIB_ORGANIC, gib_effect);
1028 			ThrowGib (self, self->leg, damage, GIB_ORGANIC, gib_effect);
1029 			ThrowGib (self, self->leg, damage, GIB_ORGANIC, gib_effect);
1030 			ThrowGib (self, self->body, damage, GIB_ORGANIC, gib_effect);
1031 			ThrowGib (self, self->arm, damage, GIB_ORGANIC, gib_effect);
1032 			ThrowGib (self, self->arm, damage, GIB_ORGANIC, gib_effect);
1033 		}
1034 	}
1035 	else
1036 	{	// normal death
1037 		if (!self->deadflag)
1038 		{
1039 			static int i;
1040 
1041 			i = (i+1)%3;
1042 			// start a death animation
1043 			self->client->anim_priority = ANIM_DEATH;
1044 
1045 			switch (i)
1046 			{
1047 			//all player models are now using the longer set of death frames only
1048 			case 0:
1049 				self->s.frame = FRAME_death501-1;
1050 				self->client->anim_end = FRAME_death518;
1051 				break;
1052 			case 1:
1053 				self->s.frame = FRAME_death601-1;
1054 				self->client->anim_end = FRAME_death620;
1055 				break;
1056 			case 2:
1057 				self->s.frame = FRAME_death401-1;
1058 				self->client->anim_end = FRAME_death422;
1059 				break;
1060 
1061 			}
1062 			gi.sound (self, CHAN_VOICE, gi.soundindex(va("*death%i.wav", (rand()%4)+1)), 1, ATTN_NORM, 0);
1063 		}
1064 	}
1065 
1066 	gi.sound (self, CHAN_VOICE, gi.soundindex("misc/death.wav"), 1, ATTN_STATIC, 0);
1067 
1068 	self->deadflag = DEAD_DEAD;
1069 	self->takedamage	= DAMAGE_NO;
1070 
1071 	gi.linkentity (self);
1072 }
1073 
1074 //=======================================================================
1075 
1076 /*
1077 ==============
1078 InitClientPersistant
1079 
1080 This is only called when the game first initializes in single player,
1081 but is called after each death and level change in deathmatch
1082 ==============
1083 */
InitClientPersistant(gclient_t * client)1084 void InitClientPersistant (gclient_t *client)
1085 {
1086 	gitem_t		*item;
1087 	int			queue=0;
1088 
1089 	if(g_duel->integer) //need to save this off in duel mode.  Potentially dangerous?
1090 		queue = client->pers.queue;
1091 
1092 	memset (&client->pers, 0, sizeof(client->pers));
1093 
1094 	if(g_duel->integer)
1095 		client->pers.queue = queue;
1096 
1097 #ifdef ALTERIA
1098 	item = FindItem("Warriorpunch");
1099 #else
1100 	if(!rocket_arena->integer && !g_tactical->integer)
1101 	{	//gets a violator, unless RA or Tactical
1102 		item = FindItem("Violator");
1103 		client->pers.selected_item = ITEM_INDEX(item);
1104 		client->pers.inventory[client->pers.selected_item] = 1;
1105 		client->pers.weapon = item;
1106 	}
1107 
1108 	//mutator - will need to have item
1109 	if(instagib->integer)
1110 	{
1111 		client->pers.inventory[ITEM_INDEX(FindItem("Alien Disruptor"))] = 1;
1112 		client->pers.inventory[ITEM_INDEX(FindItem("cells"))] = g_maxcells->value;
1113 		item = FindItem("Alien Disruptor");
1114 	}
1115 	else if(rocket_arena->integer)
1116 	{
1117 		client->pers.inventory[ITEM_INDEX(FindItem("Rocket Launcher"))] = 1;
1118 		client->pers.inventory[ITEM_INDEX(FindItem("rockets"))] = g_maxrockets->value;
1119 		item = FindItem("Rocket Launcher");
1120 	}
1121 	else if (insta_rockets->integer )
1122 	{
1123 		client->pers.inventory[ITEM_INDEX(FindItem("Rocket Launcher"))] = 1;
1124 		client->pers.inventory[ITEM_INDEX(FindItem("rockets"))] = g_maxrockets->value;
1125 		item = FindItem("Rocket Launcher");
1126 		client->pers.inventory[ITEM_INDEX(FindItem("Alien Disruptor"))] = 1;
1127 		client->pers.inventory[ITEM_INDEX(FindItem("cells"))] = g_maxcells->value;
1128 		item = FindItem("Alien Disruptor");
1129 	}
1130 	else
1131 		item = FindItem("Blaster");
1132 #endif
1133 	client->pers.selected_item = ITEM_INDEX(item);
1134 	client->pers.inventory[client->pers.selected_item] = 1;
1135 
1136 	client->pers.weapon = item;
1137 
1138 	if(excessive->value)
1139 	{
1140 		//Allow custom health, even in excessive.
1141 		client->pers.health 		= g_spawnhealth->value * 3;
1142 		client->pers.max_bullets 	= g_maxbullets->value * 2.5;
1143 		client->pers.max_shells		= g_maxshells->value * 5;
1144 		client->pers.max_rockets	= g_maxrockets->value * 10;
1145 		client->pers.max_grenades	= g_maxgrenades->value * 10;
1146 		client->pers.max_cells		= g_maxcells->value * 2.5;
1147 		client->pers.max_slugs		= g_maxslugs->value * 10;
1148 		client->pers.max_seekers	= g_maxseekers->value * 2;
1149 		client->pers.max_bombs		= g_maxbombs->value * 2;
1150 
1151 		client->pers.inventory[ITEM_INDEX(FindItem("Rocket Launcher"))] = 1;
1152 		client->pers.inventory[ITEM_INDEX(FindItem("rockets"))] = g_maxrockets->value * 10;
1153 		client->pers.inventory[ITEM_INDEX(FindItem("Pulse Rifle"))] = 1;
1154 		client->pers.inventory[ITEM_INDEX(FindItem("bullets"))] = g_maxbullets->value * 2.5;
1155 		client->pers.inventory[ITEM_INDEX(FindItem("Alien Disruptor"))] = 1;
1156 		client->pers.inventory[ITEM_INDEX(FindItem("Disruptor"))] = 1;
1157 		client->pers.inventory[ITEM_INDEX(FindItem("cells"))] = g_maxcells->value * 2.5;
1158 		client->pers.inventory[ITEM_INDEX(FindItem("Alien Smartgun"))] = 1;
1159 		client->pers.inventory[ITEM_INDEX(FindItem("alien smart grenade"))] = g_maxshells->value * 5;
1160 		client->pers.inventory[ITEM_INDEX(FindItem("Alien Vaporizer"))] = 1;
1161 		client->pers.inventory[ITEM_INDEX(FindItem("slugs"))] = g_maxslugs->value * 10;
1162 		client->pers.inventory[ITEM_INDEX(FindItem("Flame Thrower"))] = 1;
1163 		client->pers.inventory[ITEM_INDEX(FindItem("napalm"))] = g_maxgrenades->value * 10;
1164 		client->pers.inventory[ITEM_INDEX(FindItem("Minderaser"))] = 1;
1165 		client->pers.inventory[ITEM_INDEX(FindItem("seekers"))] = g_maxseekers->value * 2;
1166 		client->pers.inventory[ITEM_INDEX(FindItem("bombs"))] = g_maxbombs->value * 2;
1167 	}
1168 	else
1169 	{
1170 		client->pers.health 		= g_spawnhealth->value;
1171 		client->pers.max_bullets 	= g_maxbullets->value;
1172 		client->pers.max_shells		= g_maxshells->value;
1173 		client->pers.max_rockets	= g_maxrockets->value;
1174 		client->pers.max_grenades	= g_maxgrenades->value;
1175 		client->pers.max_cells		= g_maxcells->value;
1176 		client->pers.max_slugs		= g_maxslugs->value;
1177 		client->pers.max_seekers	= g_maxseekers->value;
1178 		client->pers.max_bombs		= g_maxbombs->value;
1179 	}
1180 
1181 	if(vampire->value)
1182 		client->pers.max_health = g_maxhealth->value * 2;
1183 	else if(excessive->value)
1184 		client->pers.max_health = g_maxhealth->value * 3;
1185 	else
1186 		client->pers.max_health = g_maxhealth->value;
1187 
1188 	if(grapple->value)
1189 	{
1190 		item = FindItem("Grapple");
1191 		client->pers.inventory[ITEM_INDEX(item)] = 1;
1192 	}
1193 
1194 	//if powered up, give back powerups
1195 	if(client->resp.powered)
1196 	{
1197 		item = FindItem("Invisibility");
1198 		client->pers.inventory[ITEM_INDEX(item)] += 1;
1199 
1200 		item = FindItem("Sproing");
1201 		client->pers.inventory[ITEM_INDEX(item)] += 1;
1202 
1203 		item = FindItem("Haste");
1204 		client->pers.inventory[ITEM_INDEX(item)] += 1;
1205 	}
1206 
1207 	client->pers.connected = true;
1208 }
1209 
1210 
InitClientResp(gclient_t * client)1211 void InitClientResp (gclient_t *client)
1212 {
1213 	memset (&client->resp, 0, sizeof(client->resp));
1214 	client->resp.enterframe = level.framenum;
1215 }
1216 
1217 /*
1218 ==================
1219 SaveClientData
1220 
1221 Some information that should be persistant, like health,
1222 is still stored in the edict structure, so it needs to
1223 be mirrored out to the client structure before all the
1224 edicts are wiped.
1225 ==================
1226 */
SaveClientData(void)1227 void SaveClientData (void)
1228 {
1229 	int		i;
1230 	edict_t	*ent;
1231 
1232 	for (i=0 ; i<game.maxclients ; i++)
1233 	{
1234 		ent = &g_edicts[1+i];
1235 		if (!ent->inuse)
1236 			continue;
1237 		game.clients[i].pers.health = ent->health;
1238 		game.clients[i].pers.max_health = ent->max_health;
1239 	}
1240 }
1241 
FetchClientEntData(edict_t * ent)1242 void FetchClientEntData (edict_t *ent)
1243 {
1244 	ent->health = ent->client->pers.health;
1245 	ent->max_health = ent->client->pers.max_health;
1246 }
1247 
1248 
1249 
1250 /*
1251 =======================================================================
1252 
1253   SelectSpawnPoint
1254 
1255 =======================================================================
1256 */
1257 
1258 /*
1259 ================
1260 PlayersRangeFromSpot
1261 
1262 Returns the distance to the nearest player from the given spot
1263 ================
1264 */
PlayersRangeFromSpot(edict_t * spot)1265 float	PlayersRangeFromSpot (edict_t *spot)
1266 {
1267 	edict_t	*player;
1268 	float	bestplayerdistance;
1269 	vec3_t	v;
1270 	int		n;
1271 	float	playerdistance;
1272 
1273 
1274 	bestplayerdistance = 9999999;
1275 
1276 	for (n = 1; n <= g_maxclients->value; n++)
1277 	{
1278 		player = &g_edicts[n];
1279 
1280 		if (!player->inuse)
1281 			continue;
1282 
1283 		if (player->health <= 0)
1284 			continue;
1285 
1286 		VectorSubtract (spot->s.origin, player->s.origin, v);
1287 		playerdistance = VectorLength (v);
1288 
1289 		if (playerdistance < bestplayerdistance)
1290 			bestplayerdistance = playerdistance;
1291 	}
1292 
1293 	return bestplayerdistance;
1294 }
1295 
1296 /*
1297 ================
1298 SelectRandomDeathmatchSpawnPoint
1299 
1300 go to a random point, but NOT the two points closest
1301 to other players
1302 ================
1303 */
SelectRandomDeathmatchSpawnPoint(void)1304 edict_t *SelectRandomDeathmatchSpawnPoint (void)
1305 {
1306 	edict_t	*spot, *spot1, *spot2;
1307 	int		count = 0;
1308 	int		selection;
1309 	float	range, range1, range2;
1310 
1311 	spot = NULL;
1312 	range1 = range2 = 99999;
1313 	spot1 = spot2 = NULL;
1314 
1315 	while ((spot = G_Find (spot, FOFS(classname), "info_player_deathmatch")) != NULL)
1316 	{
1317 		count++;
1318 		range = PlayersRangeFromSpot(spot);
1319 		if (range < range1)
1320 		{
1321 			range1 = range;
1322 			spot1 = spot;
1323 		}
1324 		else if (range < range2)
1325 		{
1326 			range2 = range;
1327 			spot2 = spot;
1328 		}
1329 	}
1330 
1331 	if (!count)
1332 		return NULL;
1333 
1334 	if (count <= 2)
1335 	{
1336 		spot1 = spot2 = NULL;
1337 	}
1338 	else
1339 	{
1340 		if ( spot1 ) {
1341 			count--;
1342 		}
1343 		if ( spot2 ) {
1344 			count--;
1345 		}
1346 	}
1347 
1348 	selection = rand() % count;
1349 
1350 	spot = NULL;
1351 	do
1352 	{
1353 		spot = G_Find (spot, FOFS(classname), "info_player_deathmatch");
1354 		if (spot == spot1 || spot == spot2)
1355 			selection++;
1356 	} while(selection--);
1357 
1358 	return spot;
1359 }
1360 /*
1361 ================
1362 SelectRandomDeathmatchSpawnPoint
1363 
1364 go to a random point, but NOT the two points closest
1365 to other players
1366 ================
1367 */
SelectRandomCTFSpawnPoint(void)1368 edict_t *SelectRandomCTFSpawnPoint (void)
1369 {
1370 	edict_t	*spot, *spot1, *spot2;
1371 	int		count = 0;
1372 	int		selection;
1373 	float	range, range1, range2;
1374 	char	whichteam[32];
1375 
1376 	spot = NULL;
1377 	range1 = range2 = 99999;
1378 	spot1 = spot2 = NULL;
1379 
1380 	if(random() < 0.5)
1381 		strcpy(whichteam, "info_player_red");
1382 	else
1383 		strcpy(whichteam, "info_player_blue");
1384 
1385 	while ((spot = G_Find (spot, FOFS(classname), whichteam)) != NULL)
1386 	{
1387 		count++;
1388 		range = PlayersRangeFromSpot(spot);
1389 		if (range < range1)
1390 		{
1391 			range1 = range;
1392 			spot1 = spot;
1393 		}
1394 		else if (range < range2)
1395 		{
1396 			range2 = range;
1397 			spot2 = spot;
1398 		}
1399 	}
1400 
1401 	if (!count)
1402 		return NULL;
1403 
1404 	if (count <= 2)
1405 	{
1406 		spot1 = spot2 = NULL;
1407 	}
1408 	else
1409 	{
1410 		if ( spot1 ) {
1411 			count--;
1412 		}
1413 		if ( spot2 ) {
1414 			count--;
1415 		}
1416 	}
1417 
1418 	selection = rand() % count;
1419 
1420 	spot = NULL;
1421 	do
1422 	{
1423 		spot = G_Find (spot, FOFS(classname), whichteam);
1424 		if (spot == spot1 || spot == spot2)
1425 			selection++;
1426 	} while(selection--);
1427 
1428 	return spot;
1429 }
1430 /*
1431 ================
1432 SelectFarthestDeathmatchSpawnPoint
1433 
1434 ================
1435 */
SelectFarthestDeathmatchSpawnPoint(void)1436 edict_t *SelectFarthestDeathmatchSpawnPoint (void)
1437 {
1438 	edict_t	*bestspot;
1439 	float	bestdistance, bestplayerdistance;
1440 	edict_t	*spot;
1441 
1442 
1443 	spot = NULL;
1444 	bestspot = NULL;
1445 	bestdistance = 0;
1446 	while ((spot = G_Find (spot, FOFS(classname), "info_player_deathmatch")) != NULL)
1447 	{
1448 		bestplayerdistance = PlayersRangeFromSpot (spot);
1449 
1450 		if (bestplayerdistance > bestdistance)
1451 		{
1452 			bestspot = spot;
1453 			bestdistance = bestplayerdistance;
1454 		}
1455 	}
1456 
1457 	if (bestspot)
1458 	{
1459 		return bestspot;
1460 	}
1461 
1462 	// if there is a player just spawned on each and every start spot
1463 	// we have no choice to turn one into a telefrag meltdown
1464 	spot = G_Find (NULL, FOFS(classname), "info_player_deathmatch");
1465 
1466 	return spot;
1467 }
1468 
SelectDeathmatchSpawnPoint(void)1469 edict_t *SelectDeathmatchSpawnPoint (void)
1470 {
1471 	if ( dmflags->integer & DF_SPAWN_FARTHEST)
1472 		return SelectFarthestDeathmatchSpawnPoint ();
1473 	else
1474 		return SelectRandomDeathmatchSpawnPoint ();
1475 }
1476 
1477 /*
1478 ================
1479 SelectCTFSpawnPoint
1480 
1481 go to a ctf point, but NOT the two points closest
1482 to other players
1483 ================
1484 */
SelectCTFSpawnPoint(edict_t * ent)1485 edict_t *SelectCTFSpawnPoint (edict_t *ent)
1486 {
1487 	edict_t	*spot, *spot1, *spot2;
1488 	int		count = 0;
1489 	int		selection;
1490 	float	range, range1, range2;
1491 	char	*cname;
1492 
1493 	if(g_tactical->value)
1494 	{
1495 		if(ent->ctype == 1)
1496 			cname = "info_player_red";
1497 		else
1498 			cname = "info_player_blue";
1499 	}
1500 	else
1501 	{
1502 		switch (ent->dmteam)
1503 		{
1504 			case RED_TEAM:
1505 				cname = "info_player_red";
1506 				break;
1507 			case BLUE_TEAM:
1508 				cname = "info_player_blue";
1509 				break;
1510 			case NO_TEAM:
1511 			default:
1512 				return SelectRandomCTFSpawnPoint();
1513 		}
1514 	}
1515 
1516 	spot = NULL;
1517 	range1 = range2 = 99999;
1518 	spot1 = spot2 = NULL;
1519 
1520 	while ((spot = G_Find (spot, FOFS(classname), cname)) != NULL)
1521 	{
1522 		count++;
1523 		range = PlayersRangeFromSpot(spot);
1524 		if (range < range1)
1525 		{
1526 			range1 = range;
1527 			spot1 = spot;
1528 		}
1529 		else if (range < range2)
1530 		{
1531 			range2 = range;
1532 			spot2 = spot;
1533 		}
1534 	}
1535 
1536 	if (!count)
1537 		return SelectRandomDeathmatchSpawnPoint();
1538 
1539 	if (count <= 2)
1540 	{
1541 		spot1 = spot2 = NULL;
1542 	}
1543 	else
1544 		count -= 2;
1545 
1546 	selection = rand() % count;
1547 
1548 	spot = NULL;
1549 	do
1550 	{
1551 		spot = G_Find (spot, FOFS(classname), cname);
1552 		if (spot == spot1 || spot == spot2)
1553 			selection++;
1554 	} while(selection--);
1555 
1556 	return spot;
1557 }
1558 
1559 
1560 /*
1561 ===========
1562 SelectSpawnPoint
1563 
1564 Chooses a player start, deathmatch start, coop start, etc
1565 ============
1566 */
SelectSpawnPoint(edict_t * ent,vec3_t origin,vec3_t angles)1567 void	SelectSpawnPoint (edict_t *ent, vec3_t origin, vec3_t angles)
1568 {
1569 	edict_t	*spot = NULL;
1570 
1571 	if (deathmatch->value)
1572 	{
1573 		if (g_tactical->value || ctf->value || tca->value || cp->value || (dmflags->integer & DF_SKINTEAMS))
1574 		{
1575 			spot = SelectCTFSpawnPoint(ent);
1576 			if(!spot)
1577 				spot = SelectDeathmatchSpawnPoint ();
1578 		}
1579 		else
1580 		{
1581 			spot = SelectDeathmatchSpawnPoint ();
1582 			if(!spot)
1583 				spot = SelectCTFSpawnPoint(ent); //dm on team based maps
1584 		}
1585 	}
1586 
1587 	// find a single player start spot
1588 	if (!spot)
1589 	{
1590 		spot = G_Find (spot, FOFS(classname), "info_player_start");
1591 		if (!spot)
1592 			gi.error ("Couldn't find spawn point!");
1593 	}
1594 
1595 	VectorCopy (spot->s.origin, origin);
1596 	origin[2] += 9;
1597 	VectorCopy (spot->s.angles, angles);
1598 }
1599 
1600 //======================================================================
1601 
1602 
InitBodyQue(void)1603 void InitBodyQue (void)
1604 {
1605 	int		i;
1606 	edict_t	*ent;
1607 
1608 	level.body_que = 0;
1609 	for (i=0; i<BODY_QUEUE_SIZE ; i++)
1610 	{
1611 		ent = G_Spawn();
1612 		ent->classname = "bodyque";
1613 	}
1614 }
1615 
1616 /*
1617 =============
1618 BodySink
1619 
1620 After sitting around for five seconds, fall into the ground and dissapear
1621 =============
1622 */
BodySink(edict_t * ent)1623 void BodySink( edict_t *ent ) {
1624 	if ( level.time - ent->timestamp > 10.5 ) {
1625 		// the body ques are never actually freed, they are just unlinked
1626 		gi.unlinkentity( ent );
1627 		ent->s.modelindex = 0; //for good measure
1628 		ent->s.modelindex2 = 0;
1629 		ent->s.modelindex3 = 0;
1630 		ent->s.modelindex4 = 0;
1631 		ent->s.effects = 0;
1632 		return;
1633 	}
1634 	ent->nextthink = level.time + .1;
1635 	ent->s.origin[2] -= 1;
1636 	ent->s.effects |= EF_COLOR_SHELL;
1637 	ent->s.renderfx |= RF_SHELL_GREEN;
1638 	ent->solid = SOLID_NOT; //don't gib sinking bodies
1639 }
1640 
body_die(edict_t * self,edict_t * inflictor,edict_t * attacker,int damage,vec3_t point)1641 void body_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
1642 {
1643 	self->s.modelindex3 = 0;
1644 	self->s.modelindex4 = 0;
1645 
1646 	self->takedamage = DAMAGE_NO;
1647 	self->solid = SOLID_NOT;
1648 	self->s.effects = EF_GIB;
1649 	self->s.sound = 0;
1650 	self->flags |= FL_NO_KNOCKBACK;
1651 
1652 	if (self->client)	// bodies in the queue don't have a client anymore
1653 	{
1654 		self->client->anim_priority = ANIM_DEATH;
1655 		self->client->anim_end = self->s.frame;
1656 	}
1657 	else
1658 	{
1659 		self->think = NULL;
1660 		self->nextthink = 0;
1661 	}
1662 
1663 	gi.linkentity (self);
1664 
1665   }
1666 
1667 
CopyToBodyQue(edict_t * ent)1668 void CopyToBodyQue (edict_t *ent)
1669 {
1670 	edict_t		*body;
1671 
1672 	// grab a body que and cycle to the next one
1673 	body = &g_edicts[g_maxclients->integer + level.body_que + 1];
1674 	level.body_que = (level.body_que + 1) % BODY_QUEUE_SIZE;
1675 
1676 	gi.unlinkentity (ent);
1677 
1678 	gi.unlinkentity (body);
1679 	body->s = ent->s;
1680 	body->s.number = body - g_edicts;
1681 
1682 	body->svflags = ent->svflags;
1683 
1684 	VectorCopy (ent->mins, body->mins);
1685 	VectorCopy (ent->maxs, body->maxs);
1686 	VectorCopy (ent->absmin, body->absmin);
1687 	VectorCopy (ent->absmax, body->absmax);
1688 	VectorCopy (ent->size, body->size);
1689 	body->solid = SOLID_NOT;
1690 	body->clipmask = ent->clipmask;
1691 	body->owner = ent->owner;
1692 	body->movetype = ent->movetype;
1693 	body->die = body_die;
1694 	body->takedamage = DAMAGE_YES;
1695 	body->ctype = ent->ctype;
1696 	body->timestamp = level.time;
1697 	body->nextthink = level.time + 5;
1698 	body->think = BodySink;
1699 
1700 	gi.linkentity (body);
1701 }
1702 
1703 
respawn(edict_t * self)1704 void respawn (edict_t *self)
1705 {
1706 	if (deathmatch->value)
1707 	{
1708 
1709 // ACEBOT_ADD special respawning code
1710 		if (self->is_bot)
1711 		{
1712 			ACESP_Respawn (self);
1713 			return;
1714 		}
1715 // ACEBOT_END
1716 
1717 		//spectator mode
1718 		// spectator's don't leave bodies
1719 		if (self->movetype != MOVETYPE_NOCLIP)
1720 			CopyToBodyQue (self);
1721 		//end spectator mode
1722 		self->svflags &= ~SVF_NOCLIENT;
1723 		PutClientInServer (self);
1724 
1725 		// add a teleportation effect
1726 		self->s.event = EV_PLAYER_TELEPORT;
1727 
1728 		// hold in place briefly
1729 		self->client->ps.pmove.pm_flags = PMF_TIME_TELEPORT;
1730 		self->client->ps.pmove.pm_time = 14;
1731 
1732 		self->client->respawn_time = level.time;
1733 
1734 		return;
1735 	}
1736 
1737 	// restart the entire server
1738 	gi.AddCommandString ("menu_loadgame\n");
1739 }
1740 
1741 //spectator mode
spectator_respawn(edict_t * ent)1742 void spectator_respawn (edict_t *ent)
1743 {
1744 	int i, numspec;
1745 
1746 	// if the user wants to become a spectator, make sure he doesn't
1747 	// exceed max_spectators
1748 
1749 	if (ent->client->pers.spectator) {
1750 		char *value = Info_ValueForKey (ent->client->pers.userinfo, "spectator_password");
1751 		if (*spectator_password->string &&
1752 			strcmp(spectator_password->string, "none") &&
1753 			strcmp(spectator_password->string, value)) {
1754 			gi.cprintf(ent, PRINT_HIGH, "%s", "Spectator password incorrect.\n");
1755 			ent->client->pers.spectator = 0;
1756 			gi.WriteByte (svc_stufftext);
1757 			gi.WriteString ("spectator 0\n");
1758 			gi.unicast(ent, true);
1759 			return;
1760 		}
1761 
1762 		// count spectators
1763 		for (i = 1, numspec = 0; i <= g_maxclients->value; i++)
1764 			if (g_edicts[i].inuse && g_edicts[i].client->pers.spectator)
1765 				numspec++;
1766 
1767 		if (numspec >= maxspectators->value) {
1768 			gi.cprintf(ent, PRINT_HIGH, "%s", "Server spectator limit is full.");
1769 			ent->client->pers.spectator = 0;
1770 			// reset his spectator var
1771 			gi.WriteByte (svc_stufftext);
1772 			gi.WriteString ("spectator 0\n");
1773 			gi.unicast(ent, true);
1774 			return;
1775 		}
1776 	} else {
1777 		// he was a spectator and wants to join the game
1778 		// he must have the right password
1779 		char *value = Info_ValueForKey (ent->client->pers.userinfo, "password");
1780 		if (*password->string && strcmp(password->string, "none") &&
1781 			strcmp(password->string, value))
1782 		{
1783 			gi.cprintf(ent, PRINT_HIGH, "%s", "Password incorrect.\n");
1784 			ent->client->pers.spectator = 1;
1785 			gi.WriteByte (svc_stufftext);
1786 			gi.WriteString ("spectator 1\n");
1787 			gi.unicast(ent, true);
1788 			return;
1789 		}
1790 	}
1791 
1792 	/* Remove deathcam if changed to spectator after death */
1793 	if (ent->client->pers.spectator && ent->deadflag)
1794 		DeathcamRemove (ent, "off");
1795 
1796 	// clear client on respawn
1797 	ent->client->resp.score = 0;
1798 
1799 	ent->svflags &= ~SVF_NOCLIENT;
1800 	PutClientInServer (ent);
1801 
1802 	// add a teleportation effect
1803 	if (!ent->client->pers.spectator)  {
1804 		// send effect
1805 		gi.WriteByte (svc_muzzleflash);
1806 		gi.WriteShort (ent-g_edicts);
1807 		gi.WriteByte (MZ_LOGIN);
1808 		gi.multicast (ent->s.origin, MULTICAST_PVS);
1809 
1810 		// hold in place briefly
1811 		ent->client->ps.pmove.pm_flags = PMF_TIME_TELEPORT;
1812 		ent->client->ps.pmove.pm_time = 14;
1813 	}
1814 
1815 	ent->client->respawn_time = level.time;
1816 
1817 	if (ent->client->pers.spectator)
1818 	{ // pers.spectator > 0, entering spectator mode
1819 		safe_bprintf (PRINT_HIGH, "%s has moved to the sidelines\n", ent->client->pers.netname);
1820 	}
1821 	else
1822 	{ // pers.spectator==0, leaving spectator mode
1823 		safe_bprintf (PRINT_HIGH, "%s joined the game\n", ent->client->pers.netname);
1824 	}
1825 	if ( sv_botkickthreshold && sv_botkickthreshold->integer )
1826 	{ // player entered or left playing field, update for auto botkick
1827 		ACESP_LoadBots( ent );
1828 	}
1829 
1830 
1831 }
1832 //end spectator mode
1833 
1834 //==============================================================
1835 
1836 /*
1837 ===========
1838 ParseClassFile
1839 
1840 Used in tactical mode for setting class items
1841 ===========
1842 */
1843 
ParseClassFile(char * config_file,edict_t * ent)1844 void ParseClassFile( char *config_file, edict_t *ent )
1845 {
1846 	char full_path[MAX_OSPATH];
1847 	FILE *fp;
1848 	int length;
1849 	char a_string[128];
1850 	char *buffer;
1851 	char *s;
1852 	size_t result;
1853 
1854 	if ( gi.FullPath( full_path, sizeof(full_path), config_file ) )
1855 	{
1856 
1857 		if((fp = fopen(full_path, "rb" )) == NULL)
1858 		{
1859 			return;
1860 		}
1861 		if ( fseek(fp, 0, SEEK_END) )
1862 		{ // seek error
1863 			fclose( fp );
1864 			return;
1865 		}
1866 		if ( (length = ftell(fp)) == (size_t)-1L )
1867 		{ // tell error
1868 			fclose( fp );
1869 			return;
1870 		}
1871 		if ( fseek(fp, 0, SEEK_SET) )
1872 		{ // seek error
1873 			fclose( fp );
1874 			return;
1875 		}
1876 
1877 		buffer = malloc( length + 1 );
1878 		if ( buffer != NULL )
1879 		{
1880 			buffer[length] = 0;
1881 			result = fread( buffer, length, 1, fp );
1882 			if ( result == 1 )
1883 			{
1884 				s = buffer;
1885 
1886 				strcpy( a_string, COM_Parse( &s ) );
1887 				ent->max_health = atoi(a_string);
1888 				strcpy( a_string, COM_Parse( &s ) );
1889 				ent->armor_type = atoi(a_string);
1890 				strcpy( a_string, COM_Parse( &s ) );
1891 				ent->has_bomb = atoi(a_string);
1892 				strcpy( a_string, COM_Parse( &s ) );
1893 				ent->has_detonator = atoi(a_string);
1894 				strcpy( a_string, COM_Parse( &s ) );
1895 				ent->has_minderaser = atoi(a_string);
1896 				strcpy( a_string, COM_Parse( &s ) );
1897 				ent->has_vaporizor = atoi(a_string);
1898 
1899 				//note - we may or may not need the ent vars, for now keep them
1900 				if(ent->max_health > 0)
1901 					ent->health = ent->client->pers.max_health = ent->client->pers.health = ent->max_health;
1902 				else
1903 					ent->max_health = 100;
1904 
1905 				switch(ent->armor_type)
1906 				{
1907 					default:
1908 					case 0:
1909 						break;
1910 					case 1:
1911 						ent->client->pers.inventory[ITEM_INDEX(FindItem("Jacket Armor"))] += 30;
1912 						break;
1913 					case 2:
1914 						ent->client->pers.inventory[ITEM_INDEX(FindItem("Body Armor"))] += 50;
1915 						break;
1916 					case 3:
1917 						ent->client->pers.inventory[ITEM_INDEX(FindItem("Jacket Armor"))] += 30;
1918 						break;
1919 				}
1920 
1921 				if(ent->has_minderaser)
1922 				{
1923 					ent->client->pers.inventory[ITEM_INDEX(FindItem("Minderaser"))] = 1;
1924 					ent->client->pers.inventory[ITEM_INDEX(FindItem("seekers"))] = 1;
1925 				}
1926 
1927 				if(ent->has_vaporizor)
1928 				{
1929 					ent->client->pers.inventory[ITEM_INDEX(FindItem("Alien Vaporizer"))] = 1;
1930 					ent->client->pers.inventory[ITEM_INDEX(FindItem("slugs"))] = 4;
1931 				}
1932 			}
1933 
1934 			free( buffer );
1935 		}
1936 		fclose( fp );
1937 	}
1938 
1939 	return;
1940 }
1941 
1942 /*
1943 ===========
1944 PutClientInServer
1945 
1946 Called when a player connects to a server or respawns in
1947 a deathmatch.
1948 ============
1949 */
PutClientInServer(edict_t * ent)1950 void PutClientInServer (edict_t *ent)
1951 {
1952 	vec3_t	mins = {-16, -16, -24};
1953 	vec3_t	maxs = {16, 16, 32};
1954 	int		index, armor_index;
1955 	vec3_t	spawn_origin, spawn_angles;
1956 	gclient_t	*client;
1957 	gitem_t		*item;
1958 	int		i, done;
1959 	client_persistant_t	saved;
1960 	client_respawn_t	resp;
1961 	char	*info;
1962 	char playermodel[MAX_OSPATH] = " ";
1963 	char modelpath[MAX_OSPATH] = " ";
1964 	FILE *file;
1965 	char userinfo[MAX_INFO_STRING];
1966 
1967 	// find a spawn point
1968 	// do it before setting health back up, so farthest
1969 	// ranging doesn't count this client
1970 	if(!g_tactical->integer)
1971 		SelectSpawnPoint (ent, spawn_origin, spawn_angles);
1972 
1973 	index = ent-g_edicts-1;
1974 	client = ent->client;
1975 	client->is_bot = 0;
1976 	client->kill_streak = 0;
1977 	client->homing_shots = 0;
1978 	client->mapvote = 0;
1979 	client->lasttaunttime = 0;
1980 	client->rayImmunity = false;
1981 
1982 	resp = client->resp;
1983 	memcpy (userinfo, client->pers.userinfo, sizeof(userinfo));
1984 	InitClientPersistant (client);
1985 	ClientUserinfoChanged (ent, userinfo, SPAWN);
1986 
1987 	// clear everything but the persistant data
1988 	saved = client->pers;
1989 	memset (client, 0, sizeof(*client));
1990 	client->pers = saved;
1991 	if (client->pers.health <= 0)
1992 		InitClientPersistant(client);
1993 	client->resp = resp;
1994 
1995 	// copy some data from the client to the entity
1996 	FetchClientEntData (ent);
1997 
1998 	// clear entity values
1999 	ent->groundentity = NULL;
2000 	ent->client = &game.clients[index];
2001 	if(g_spawnprotect->value)
2002 		ent->client->spawnprotected = true;
2003 	ent->takedamage = DAMAGE_AIM;
2004 	ent->movetype = MOVETYPE_WALK;
2005 	ent->viewheight = 22;
2006 	ent->inuse = true;
2007 	ent->classname = "player";
2008 	ent->mass = 200;
2009 	ent->solid = SOLID_BBOX;
2010 	ent->deadflag = DEAD_NO;
2011 	ent->air_finished = level.time + 12;
2012 	ent->clipmask = MASK_PLAYERSOLID;
2013 	ent->model = "players/martianenforcer/tris.md2";
2014 	ent->pain = player_pain;
2015 	ent->die = player_die;
2016 	ent->waterlevel = 0;
2017 	ent->watertype = 0;
2018 	ent->flags &= ~FL_NO_KNOCKBACK;
2019 	ent->svflags &= ~SVF_DEADMONSTER;
2020 // ACEBOT_ADD
2021 	ent->is_bot = false;
2022 	ent->last_node = -1;
2023 	ent->is_jumping = false;
2024 // ACEBOT_END
2025 	//vehicles
2026 	ent->in_vehicle = false;
2027 
2028 	//deathball
2029 	ent->in_deathball = false;
2030 
2031 	//anti-camp
2032 	ent->suicide_timeout = level.time + 10.0;
2033 	VectorClear (ent->velocity_accum);
2034 	ent->old_velocities_current = -1;
2035 	ent->old_velocities_count = 0;
2036 
2037 	VectorCopy (mins, ent->mins);
2038 	VectorCopy (maxs, ent->maxs);
2039 	VectorClear (ent->velocity);
2040 
2041 	// clear playerstate values
2042 	memset (&ent->client->ps, 0, sizeof(client->ps));
2043 
2044 	//remove these if there are there
2045 	if(ent->client->oldplayer)
2046 		G_FreeEdict (ent->client->oldplayer);
2047 	if(ent->client->chasecam)
2048 		G_FreeEdict (ent->client->chasecam);
2049 
2050 	client->ps.fov = atoi(Info_ValueForKey(client->pers.userinfo, "fov"));
2051 	if (client->ps.fov < 1)
2052 		client->ps.fov = 90;
2053 	else if (client->ps.fov > 160)
2054 		client->ps.fov = 160;
2055 
2056 	client->ps.gunindex = gi.modelindex(client->pers.weapon->view_model);
2057 
2058 	// clear entity state values
2059 	ent->s.effects = 0;
2060 	ent->s.skinnum = ent - g_edicts - 1;
2061 	ent->s.modelindex = 255;		// will use the skin specified model
2062 	ent->s.modelindex2 = 255;		// custom gun model
2063 	info = Info_ValueForKey (ent->client->pers.userinfo, "skin");
2064 
2065 	i = 0;
2066 	done = false;
2067 	strcpy(playermodel, " ");
2068 	while(!done)
2069 	{
2070 		if((info[i] == '/') || (info[i] == '\\'))
2071 			done = true;
2072 		playermodel[i] = info[i];
2073 		if(i > 62)
2074 			done = true;
2075 		i++;
2076 	}
2077 	playermodel[i-1] = 0;
2078 
2079 	sprintf(modelpath, "players/%s/helmet.md2", playermodel);
2080 	Q2_FindFile (modelpath, &file); //does a helmet exist?
2081 	if(file)
2082 	{
2083 		sprintf(modelpath, "players/%s/helmet.md2", playermodel);
2084 		ent->s.modelindex3 = gi.modelindex(modelpath);
2085 		fclose(file);
2086 	}
2087 	else
2088 		ent->s.modelindex3 = 0;
2089 
2090 	ent->s.modelindex4 = 0;
2091 
2092 	//check for class file
2093 #ifdef ALTERIA
2094 	ent->ctype = 0; //wizard is default
2095 	sprintf(modelpath, "players/%s/human", playermodel);
2096 	Q2_FindFile (modelpath, &file);
2097 	if(file)
2098 	{	//warrior
2099 		ent->ctype = 1;
2100 
2101 		ent->health = ent->max_health = client->pers.max_health = client->pers.health = 100;
2102 		armor_index = ITEM_INDEX(FindItem("Jacket Armor"));
2103 		client->pers.inventory[armor_index] += 30;
2104 		client->pers.inventory[ITEM_INDEX(FindItem("Warriorpunch"))] = 1;
2105 		item = FindItem("Warriorpunch");
2106 		client->pers.selected_item = ITEM_INDEX(item);
2107 		client->pers.inventory[client->pers.selected_item] = 1;
2108 		client->pers.weapon = item;
2109 
2110 		fclose(file);
2111 	}
2112 	else
2113 	{	//wizard
2114 
2115 		ent->health = ent->max_health = client->pers.max_health = client->pers.health = 150;
2116 		client->pers.inventory[ITEM_INDEX(FindItem("Wizardpunch"))] = 1;
2117 		//client->pers.inventory[ITEM_INDEX(FindItem("cells"))] = 100; //to to - blue or yellow mana
2118 		item = FindItem("Wizardpunch");
2119 		client->pers.selected_item = ITEM_INDEX(item);
2120 		client->pers.inventory[client->pers.selected_item] = 1;
2121 		client->pers.weapon = item;
2122 	}
2123 #else
2124 	ent->ctype = 0; //alien is default
2125 	sprintf(modelpath, "players/%s/human", playermodel);
2126 	sprintf(ent->charModel, playermodel);
2127 	Q2_FindFile (modelpath, &file);
2128 	if(file)
2129 	{
2130 		fclose(file);
2131 
2132 		//human
2133 		ent->ctype = 1;
2134 		if(g_tactical->integer || (classbased->value && !(rocket_arena->integer || instagib->integer || insta_rockets->value || excessive->value)))
2135 		{
2136 			if(g_tactical->integer)
2137 			{
2138 				//read class file(tactical only)
2139 				//example:
2140 				//100-150 (health)
2141 				//0-3 (armor type)
2142 				//0-1 (has bomb)
2143 				//0-1 (has detonator)
2144 				//0-1 (has mind eraser)
2145 				//0-1 (has vaporizor)
2146 
2147 				ParseClassFile(modelpath, ent);
2148 				if(ent->has_bomb)
2149 				{
2150 					ent->client->pers.inventory[ITEM_INDEX(FindItem("Human Bomb"))] = 1;
2151 					ent->client->pers.inventory[ITEM_INDEX(FindItem("bombs"))] = 1; //tactical note - humans will use same ammo, etc, just different weapons
2152 				}
2153 				item = FindItem("Blaster");
2154 			}
2155 			else
2156 			{
2157 				ent->health = ent->max_health = client->pers.max_health = client->pers.health = 100;
2158 				armor_index = ITEM_INDEX(FindItem("Jacket Armor"));
2159 				client->pers.inventory[armor_index] += 30;
2160 
2161 				client->pers.inventory[ITEM_INDEX(FindItem("Rocket Launcher"))] = 1;
2162 				client->pers.inventory[ITEM_INDEX(FindItem("rockets"))] = 10;
2163 				item = FindItem("Rocket Launcher");
2164 			}
2165 			client->pers.selected_item = ITEM_INDEX(item);
2166 			client->pers.inventory[client->pers.selected_item] = 1;
2167 			client->pers.weapon = item;
2168 		}
2169 	}
2170 	else
2171 	{
2172 		//robot - not used in tactical - should we kick them out at this point, or just let them continue on the alien team?
2173 		sprintf(modelpath, "players/%s/robot", playermodel);
2174 		Q2_FindFile (modelpath, &file);
2175 		if(file && !g_tactical->integer)
2176 		{
2177 			ent->ctype = 2;
2178 			if(classbased->value && !(rocket_arena->integer || instagib->integer || insta_rockets->value || excessive->value))
2179 			{
2180 				ent->health = ent->max_health = client->pers.max_health = client->pers.health = 85;
2181 				armor_index = ITEM_INDEX(FindItem("Combat Armor"));
2182 				client->pers.inventory[armor_index] += 175;
2183 			}
2184 			fclose(file);
2185 		}
2186 		else
2187 		{
2188 			//alien
2189 			ent->ctype = 0;
2190 			if(g_tactical->integer || (classbased->value && !(rocket_arena->integer || instagib->integer || insta_rockets->value || excessive->value)))
2191 			{
2192 				ent->health = ent->max_health = client->pers.max_health = client->pers.health = 150;
2193 				if(g_tactical->integer)
2194 				{
2195 					sprintf(modelpath, "players/%s/alien", playermodel);
2196 					Q2_FindFile (modelpath, &file);
2197 					if(file)
2198 					{
2199 						ParseClassFile(modelpath, ent);
2200 						if(ent->has_bomb)
2201 						{
2202 							ent->client->pers.inventory[ITEM_INDEX(FindItem("Alien Bomb"))] = 1;
2203 							ent->client->pers.inventory[ITEM_INDEX(FindItem("bombs"))] = 1; //tactical note - humans will use same ammo, etc, just different weapons
2204 						}
2205 					}
2206 					item = FindItem("Blaster");
2207 					client->pers.selected_item = ITEM_INDEX(item);
2208 					client->pers.inventory[client->pers.selected_item] = 0;
2209 
2210 					item = FindItem("Alien Blaster");
2211 				}
2212 				else
2213 				{
2214 					client->pers.inventory[ITEM_INDEX(FindItem("Alien Disruptor"))] = 1;
2215 					client->pers.inventory[ITEM_INDEX(FindItem("cells"))] = 100;
2216 					item = FindItem("Alien Disruptor");
2217 				}
2218 				client->pers.selected_item = ITEM_INDEX(item);
2219 				client->pers.inventory[client->pers.selected_item] = 1;
2220 				client->pers.weapon = item;
2221 			}
2222 		}
2223 	}
2224 #endif
2225 
2226 	//has to be done after determining the class/team - note - we don't care about spawn distances in tactical
2227 	if(g_tactical->integer)
2228 		SelectSpawnPoint (ent, spawn_origin, spawn_angles);
2229 
2230 	client->ps.pmove.origin[0] = spawn_origin[0]*8;
2231 	client->ps.pmove.origin[1] = spawn_origin[1]*8;
2232 	client->ps.pmove.origin[2] = spawn_origin[2]*8;
2233 
2234 	ent->s.frame = 0;
2235 	VectorCopy (spawn_origin, ent->s.origin);
2236 	ent->s.origin[2] += 1;	// make sure off ground
2237 	VectorCopy (ent->s.origin, ent->s.old_origin);
2238 
2239 	// set the delta angle
2240 	for (i=0 ; i<3 ; i++)
2241 		client->ps.pmove.delta_angles[i] = ANGLE2SHORT(spawn_angles[i] - client->resp.cmd_angles[i]);
2242 
2243 	ent->s.angles[PITCH] = 0;
2244 	ent->s.angles[YAW] = spawn_angles[YAW];
2245 	ent->s.angles[ROLL] = 0;
2246 	VectorCopy (ent->s.angles, client->ps.viewangles);
2247 	VectorCopy (ent->s.angles, client->v_angle);
2248 
2249 	//spectator mode
2250 	// spawn a spectator
2251 	if ( client->pers.spectator != 0 )
2252 	{
2253 		client->chase_target = NULL;
2254 		client->resp.spectator = client->pers.spectator;
2255 		ent->movetype = MOVETYPE_NOCLIP;
2256 		ent->solid = SOLID_NOT;
2257 		ent->svflags |= SVF_NOCLIENT;
2258 		ent->client->ps.gunindex = 0;
2259 		gi.linkentity (ent);
2260 		return;
2261 	}
2262 	else if ( !g_duel->integer )
2263 		client->resp.spectator = 0;
2264 	//end spectator mode
2265 
2266 	if (!KillBox (ent))
2267 	{	// could't spawn in?
2268 	}
2269 	ent->s.event = EV_OTHER_TELEPORT; //to fix "player flash" bug
2270 	gi.linkentity (ent);
2271 
2272 	// force the current weapon up
2273 	client->newweapon = client->pers.weapon;
2274 	ChangeWeapon (ent);
2275 
2276 	client->spawnprotecttime = level.time;
2277 
2278 	//unlagged
2279 	if ( g_antilag->integer)
2280 	{
2281 		G_ResetHistory( ent );
2282 		// and this is as good a time as any to clear the saved state
2283 		client->saved.leveltime = 0;
2284 	}
2285 }
2286 
2287 //DUEL MODE
ClientPlaceInQueue(edict_t * ent)2288 void ClientPlaceInQueue(edict_t *ent)
2289 {
2290 	int		i;
2291 	int		highestpos, induel, numplayers;
2292 
2293 	highestpos = induel = numplayers = 0;
2294 
2295 	for (i = 0; i < g_maxclients->value; i++)
2296 	{
2297 		if(g_edicts[i+1].inuse && g_edicts[i+1].client)
2298 		{
2299 			if(g_edicts[i+1].client->pers.queue > highestpos) //only count players that are actually in
2300 				highestpos = g_edicts[i+1].client->pers.queue;
2301 			if(g_edicts[i+1].client->pers.queue && g_edicts[i+1].client->pers.queue < 3)
2302 				induel++;
2303 			if(g_edicts[i+1].client->pers.queue) //only count players that are actually in
2304 				numplayers++;
2305 		}
2306 	}
2307 
2308 	if(induel > 1) //make sure no more than two are in the duel at once
2309 		if(highestpos < 2)
2310 			highestpos = 2; //in case two people somehow managed to have pos 1
2311 	if(highestpos < numplayers)
2312 		highestpos = numplayers;
2313 
2314 	if(!ent->client->pers.queue)
2315 		ent->client->pers.queue = highestpos+1;
2316 }
ClientCheckQueue(edict_t * ent)2317 void ClientCheckQueue(edict_t *ent)
2318 {
2319 	int		i;
2320 	int		numplayers = 0;
2321 
2322 	if(ent->client->pers.queue > 2)
2323 	{
2324 		//everyone in line remains a spectator
2325 		ent->client->pers.spectator = ent->client->resp.spectator = 1;
2326 		ent->client->chase_target = NULL;
2327 		ent->movetype = MOVETYPE_NOCLIP;
2328 		ent->solid = SOLID_NOT;
2329 		ent->svflags |= SVF_NOCLIENT;
2330 		ent->client->ps.gunindex = 0;
2331 		gi.linkentity (ent);
2332 	}
2333 	else
2334 	{
2335 		for (i = 0; i < g_maxclients->value; i++)
2336 		{
2337 			if(g_edicts[i+1].inuse && g_edicts[i+1].client)
2338 			{
2339 				if(!g_edicts[i+1].client->pers.spectator && g_edicts[i+1].client->pers.queue)
2340 					numplayers++;
2341 			}
2342 		}
2343 		if(numplayers < 3) //only put him in if there are less than two
2344 			ent->client->pers.spectator = ent->client->resp.spectator = 0;
2345 	}
2346 }
MoveClientsDownQueue(edict_t * ent)2347 void MoveClientsDownQueue(edict_t *ent)
2348 {
2349 	int		i;
2350 	qboolean putonein = false;
2351 
2352 	for (i = 0; i < g_maxclients->value; i++)
2353 	{
2354 		//move everyone down
2355 		if(g_edicts[i+1].inuse && g_edicts[i+1].client)
2356 		{
2357 			if(g_edicts[i+1].client->pers.queue > ent->client->pers.queue)
2358 				g_edicts[i+1].client->pers.queue--;
2359 
2360 			if(!putonein && g_edicts[i+1].client->pers.queue == 2 && g_edicts[i+1].client->resp.spectator)
2361 			{
2362 				//make sure those who should be in game are
2363 				g_edicts[i+1].client->pers.spectator = g_edicts[i+1].client->resp.spectator = false;
2364 				g_edicts[i+1].svflags &= ~SVF_NOCLIENT;
2365 				g_edicts[i+1].movetype = MOVETYPE_WALK;
2366 				g_edicts[i+1].solid = SOLID_BBOX;
2367 				if(!g_edicts[i+1].is_bot)
2368 					PutClientInServer(g_edicts+i+1);
2369 				else
2370 					ACESP_PutClientInServer( g_edicts+i+1, true ); // respawn
2371 				safe_bprintf(PRINT_HIGH, "%s has entered the duel!\n", g_edicts[i+1].client->pers.netname);
2372 				putonein = true;
2373 			}
2374 		}
2375 
2376 	}
2377 	if(ent->client)
2378 		ent->client->pers.queue = 0;
2379 }
2380 //END DUEL MODE
2381 
2382 #define MAX_MOTD_SIZE	500
2383 
2384 /** @brief Read the MOTD file and send it to the client
2385  *
2386  * Try to open the MOTD file (first from the CVar setting, then from the
2387  * default location). If it is found, read it and send it to the client.
2388  *
2389  * @param ent the entity to send to
2390  *
2391  * @return true if the message was found and sent
2392  */
SendMessageOfTheDay(edict_t * ent)2393 static qboolean SendMessageOfTheDay( edict_t * ent )
2394 {
2395 	FILE		*file;
2396 	char		name[ MAX_OSPATH ];
2397 	qboolean	found;
2398 	int		size;
2399 	char		motd[ MAX_MOTD_SIZE ];
2400 
2401 	found = false;
2402 	if ( motdfile && motdfile->string && motdfile->string[0] ) {
2403 		// look for custom message of the day file
2404 		found = gi.FullPath( name, sizeof( name ),
2405 				motdfile->string );
2406 	}
2407 	if ( !found ) {
2408 		// look for default message of the day file
2409 		found = gi.FullPath( name, sizeof( name ), "motd.txt" );
2410 	}
2411 	if ( !found || (file = fopen( name, "rb" )) == NULL ) {
2412 		// No MOTD at all, or we can't read it
2413 		return false;
2414 	}
2415 
2416 	// We successfully opened the file "motd.txt" - read it and close it
2417 	size = fread( motd , 1 , MAX_MOTD_SIZE - 1 , file );
2418 	fclose( file );
2419 
2420 	// Make sure what we read is NULL-terminated
2421 	motd[ size ] = 0;
2422 
2423 	// If the file did contain data, print to the client
2424 	if ( size ) {
2425 		gi.centerprintf( ent , "%s" , motd );
2426 	}
2427 	return ( size > 0 );
2428 }
2429 
2430 
2431 /*
2432 =====================
2433 ClientBeginDeathmatch
2434 
2435 A client has just connected to the server in
2436 deathmatch mode, so clear everything out before starting them.
2437 =====================
2438 */
ClientBeginDeathmatch(edict_t * ent)2439 void ClientBeginDeathmatch (edict_t *ent)
2440 {
2441 	G_InitEdict (ent);
2442 
2443 	InitClientResp (ent->client);
2444 
2445 	ent->dmteam = NO_TEAM;
2446 
2447 	// locate ent at a spawn point
2448 	if(!ent->client->pers.spectator) //fixes invisible player bugs caused by leftover svf_noclients
2449 		ent->svflags &= ~SVF_NOCLIENT;
2450 	PutClientInServer (ent);
2451 
2452 	//kick and blackhole a player in tactical that is not using an authorized character!
2453 	if(g_tactical->integer)
2454 	{
2455 		//we want to actually check their model to be one of the valid ones we use
2456 		if(strcmp("martianenforcer", ent->charModel) && strcmp("martianwarrior", ent->charModel) && strcmp("martianoverlord", ent->charModel)
2457 			&& strcmp("lauren", ent->charModel) && strcmp("enforcer", ent->charModel) && strcmp("commander", ent->charModel))
2458 		{
2459 			if ( ent->is_bot )
2460 			{
2461 				ACESP_KickBot( ent );
2462 			}
2463 			else
2464 			{
2465 				safe_bprintf(PRINT_HIGH, "%s was kicked for using invalid character class!\n", ent->client->pers.netname);
2466 				ClientDisconnect (ent);
2467 			}
2468 		}
2469 	}
2470 
2471 	//in ctf, initially start in chase mode, and allow them to choose a team
2472 	if( TEAM_GAME )
2473 	{
2474 		ent->client->pers.spectator = 1;
2475 		ent->client->chase_target = NULL;
2476 		ent->client->resp.spectator = 1;
2477 		ent->movetype = MOVETYPE_NOCLIP;
2478 		ent->solid = SOLID_NOT;
2479 		ent->svflags |= SVF_NOCLIENT;
2480 		ent->client->ps.gunindex = 0;
2481 		gi.linkentity (ent);
2482 		//bring up scoreboard if not on a team
2483 		if(ent->dmteam == NO_TEAM)
2484 		{
2485 			ent->client->showscores = true;
2486 			CTFScoreboardMessage (ent, NULL, false);
2487 			gi.unicast (ent, true);
2488 			ent->teamset = true; // used to error check team skin
2489 		}
2490 	}
2491 
2492 	//if duel mode, then check number of existing players.  If more there are already two in the game, force
2493 	//this player to spectator mode, and assign a queue position(we can use the spectator cvar for this)
2494 	else if(g_duel->integer)
2495 	{
2496 		ClientPlaceInQueue(ent);
2497 		ClientCheckQueue(ent);
2498 	}
2499 
2500 	// send effect
2501 	gi.WriteByte (svc_muzzleflash);
2502 	gi.WriteShort (ent-g_edicts);
2503 	gi.WriteByte (MZ_LOGIN);
2504 	gi.multicast (ent->s.origin, MULTICAST_PVS);
2505 
2506 	safe_bprintf (PRINT_HIGH, "%s entered the game\n", ent->client->pers.netname);
2507 
2508 	// Send MOTD and enable MOTD protection if necessary
2509 	if ( SendMessageOfTheDay( ent ) ) {
2510 		ent->client->motd_frames = motdforce->integer;
2511 		if ( ent->client->motd_frames < 0 ) {
2512 			ent->client->motd_frames = 0;
2513 		}
2514 	} else {
2515 		ent->client->motd_frames = 0;
2516 	}
2517 
2518 	if(g_callvote->value)
2519 		safe_cprintf(ent, PRINT_HIGH, "Call voting is ^2ENABLED\n");
2520 	else
2521 		safe_cprintf(ent, PRINT_HIGH, "Call voting is ^1DISABLED\n");
2522 
2523 	if(g_antilag->value)
2524 		safe_cprintf(ent, PRINT_HIGH, "Antilag is ^2ENABLED\n");
2525 	else
2526 		safe_cprintf(ent, PRINT_HIGH, "Antilag is ^1DISABLED\n");
2527 
2528 	//check bots with each player connect
2529 	ACESP_LoadBots( ent );
2530 
2531 	// make sure all view stuff is valid
2532 	ClientEndServerFrame (ent);
2533 }
2534 
2535 
2536 /*
2537 ===========
2538 ClientBegin
2539 
2540 called when a client has finished connecting, and is ready
2541 to be placed into the game.  This will happen every level load.
2542 ============
2543 */
ClientBegin(edict_t * ent)2544 void ClientBegin (edict_t *ent)
2545 {
2546 	int		i;
2547 
2548 	ent->client = game.clients + (ent - g_edicts - 1);
2549 
2550 	for(i = 0; i < 9; i++) {
2551 		ent->client->resp.weapon_shots[i] = 0;
2552 		ent->client->resp.weapon_hits[i] = 0;
2553 	}
2554 	ent->client->kill_streak = 0;
2555 
2556 	ent->client->homing_shots = 0;
2557 
2558 	ClientBeginDeathmatch (ent);
2559 
2560 }
2561 
2562 /*
2563 ===========
2564 ClientUserInfoChanged
2565 
2566 called whenever the player updates a userinfo variable.
2567 
2568 The game can override any of the settings in place
2569 (forcing skins or names, etc) before copying it off.
2570 ============
2571 */
ClientUserinfoChanged(edict_t * ent,char * userinfo,int whereFrom)2572 void ClientUserinfoChanged (edict_t *ent, char *userinfo, int whereFrom)
2573 {
2574 	char	*s;
2575 	edict_t *cl_ent;
2576 	int		playernum;
2577 	int		i, j, k;
2578 	qboolean duplicate, done, copychar;
2579 	char playermodel[MAX_OSPATH] = " ";
2580 	char playerskin[MAX_INFO_STRING] = " ";
2581 	char modelpath[MAX_OSPATH] = " ";
2582 	char slot[4];
2583 	char tmp_playername[PLAYERNAME_SIZE];
2584 	FILE *file;
2585 	teamcensus_t teamcensus;
2586 
2587 	// check for malformed or illegal info strings
2588 	if (!Info_Validate(userinfo))
2589 	{
2590 		if(ent->dmteam == RED_TEAM)
2591 			strcpy (userinfo, "\\name\\badinfo\\skin\\martianenforcer/red");
2592 		else if(ent->dmteam == BLUE_TEAM)
2593 			strcpy (userinfo, "\\name\\badinfo\\skin\\martianenforcer/blue");
2594 		else
2595 			strcpy (userinfo, "\\name\\badinfo\\skin\\martianenforcer/default");
2596 
2597 		ent->s.modelindex3 = gi.modelindex("players/martianenforcer/helmet.md2");
2598 	}
2599 
2600 	if(whereFrom != SPAWN && whereFrom != CONNECT)
2601 		whereFrom = INGAME;
2602 
2603 	if((playervote.called || g_tactical->integer ) && whereFrom == INGAME)
2604 		return; //do not allow people to change info during votes or in tactical mode(yet)
2605 
2606 	/*if(((dmflags->integer & DF_SKINTEAMS) || ctf->value || tca->value || cp->value) && !whereFrom && (ent->dmteam != NO_TEAM)) {
2607 		safe_bprintf (PRINT_MEDIUM, "Changing settings not allowed during a team game\n");
2608 		return;
2609 	}*/
2610 
2611 	// validate and set name
2612 	s = Info_ValueForKey (userinfo, "name");
2613 	if ( s == NULL || *s == 0 )
2614 		s = "Player";
2615 	Q_strncpyz2( tmp_playername, s , sizeof(tmp_playername) );
2616 	ValidatePlayerName( tmp_playername, sizeof(tmp_playername) );
2617 	Q_strncpyz2( ent->client->pers.netname, tmp_playername,
2618 			sizeof(ent->client->pers.netname));
2619 	// end name validate
2620 
2621 	//spectator mode
2622 	if ( !g_duel->integer )
2623 	{
2624 		// never fool with spectating in duel mode
2625 		// set spectator
2626 		s = Info_ValueForKey (userinfo, "spectator");
2627 		if ( TEAM_GAME )
2628 		{ //
2629 			if ( strcmp( s, "2" ) )
2630 			{
2631 				// not special team game spectate
2632 				// so make sure it stays 0
2633 				Info_SetValueForKey( userinfo, "spectator", "0");
2634 			}
2635 			else
2636 			{
2637 				ent->client->pers.spectator = 2;
2638 			}
2639 		}
2640 		else
2641 		{
2642 			if ( deathmatch->value && *s && strcmp(s, "0") )
2643 				ent->client->pers.spectator = atoi(s);
2644 			else
2645 				ent->client->pers.spectator = 0;
2646 		}
2647 	}
2648 	//end spectator mode
2649 
2650 	// set skin
2651 	s = Info_ValueForKey (userinfo, "skin");
2652 
2653 	// do the team skin check
2654 	if ( TEAM_GAME
2655 			&& ent->client->pers.spectator != 2 && ent->client->resp.spectator != 2 )
2656 	{
2657 		copychar = false;
2658 		strcpy( playerskin, " " );
2659 		strcpy( playermodel, " " );
2660 		j = k = 0;
2661 		for ( i = 0; i <= strlen( s ) && i < MAX_OSPATH; i++ )
2662 		{
2663 				if(copychar){
2664 				playerskin[k] = s[i];
2665 				k++;
2666 			}
2667 				else {
2668 				playermodel[j] = s[i];
2669 				j++;
2670 			}
2671 			if ( s[i] == '/' )
2672 				copychar = true;
2673 		}
2674 		playermodel[j] = 0;
2675 
2676 		if ( whereFrom != SPAWN && whereFrom != CONNECT && ent->teamset )
2677 		{ // ingame and team is supposed to be set, error check skin
2678 			if ( strcmp( playerskin, "red" ) && strcmp( playerskin, "blue" ) )
2679 			{ // skin error, fix team assignment
2680 				ent->dmteam = NO_TEAM;
2681 				TeamCensus( &teamcensus ); // apply team balancing rules
2682 				ent->dmteam = teamcensus.team_for_real;
2683 				safe_bprintf( PRINT_MEDIUM,
2684 						"Invalid Team Skin!  Assigning to %s Team...\n",
2685 						(ent->dmteam == RED_TEAM ? "Red" : "Blue") );
2686 				ClientBegin( ent ); // this might work
2687 			}
2688 		}
2689 		strcpy( playerskin, (ent->dmteam == RED_TEAM ? "red" : "blue") );
2690 
2691 		if(strlen(playermodel) > 32) //something went wrong, or somebody is being malicious
2692 			strcpy(playermodel, "martianenforcer/");
2693 		strcpy(s, playermodel);
2694 		strcat(s, playerskin);
2695 		Info_SetValueForKey (userinfo, "skin", s);
2696 	}
2697 	// end skin check
2698 
2699 	playernum = ent-g_edicts-1;
2700 
2701 	//check for duplicates(but not on respawns)
2702 	duplicate = false;
2703 	if(whereFrom != SPAWN)
2704 	{
2705 		for (j=0; j<g_maxclients->value ; j++)
2706 		{
2707 			cl_ent = g_edicts + 1 + j;
2708 			if (!cl_ent->inuse)
2709 				continue;
2710 
2711 			if(!strcmp(ent->client->pers.netname, cl_ent->client->pers.netname)) {
2712 
2713 				if(playernum != j)
2714 					duplicate = true;
2715 			}
2716 		}
2717 
2718 		if(duplicate && playernum < 100)
2719 		{
2720 			//just paranoia, should never be more than 64
2721 
2722 #if defined WIN32_VARIANT
2723 			i = sprintf_s(slot, sizeof(slot), "%i", playernum);
2724 #else
2725 			i = snprintf(slot, sizeof(slot), "%i", playernum);
2726 #endif
2727 			if ( strlen(ent->client->pers.netname) < (PLAYERNAME_SIZE - i) )
2728 			{ //small enough, just add to end
2729 				strcat(ent->client->pers.netname, slot);
2730 			}
2731 			else
2732 			{ //need to lop off end first  TODO: technically, should look for color escapes
2733 				ent->client->pers.netname[ (PLAYERNAME_SIZE-1) - i ] = 0;
2734 				strcat(ent->client->pers.netname, slot);
2735 			}
2736 
2737 			Info_SetValueForKey (userinfo, "name", ent->client->pers.netname);
2738 			safe_bprintf(PRINT_HIGH, "Was a duplicate, changing name to %s\n", ent->client->pers.netname);
2739 		}
2740 	}
2741 	// end duplicate check
2742 
2743 	// combine name and skin into a configstring
2744 	gi.configstring (CS_PLAYERSKINS+playernum, va("%s\\%s", ent->client->pers.netname, s) );
2745 
2746 	s = Info_ValueForKey (userinfo, "skin");
2747 
2748 	i = 0;
2749 	done = false;
2750 	strcpy(playermodel, " ");
2751 	while(!done)
2752 	{
2753 		if((s[i] == '/') || (s[i] == '\\'))
2754 			done = true;
2755 		playermodel[i] = s[i];
2756 		if(i > 62)
2757 			done = true;
2758 		i++;
2759 	}
2760 	playermodel[i-1] = 0;
2761 
2762 	sprintf(modelpath, "players/%s/helmet.md2", playermodel);
2763 	Q2_FindFile (modelpath, &file); //does a helmet exist?
2764 	if(file)
2765 	{
2766 		sprintf(modelpath, "players/%s/helmet.md2", playermodel);
2767 		ent->s.modelindex3 = gi.modelindex(modelpath);
2768 		fclose(file);
2769 	}
2770 	else
2771 		ent->s.modelindex3 = 0;
2772 
2773 	ent->s.modelindex4 = 0;
2774 
2775 	//do gib checking here - to do - let's replace these model specific gibs with class type specific(alien/robot/human)
2776 	//check for gib file
2777 	ent->usegibs = 0; //alien is default
2778 	sprintf(modelpath, "players/%s/usegibs", playermodel);
2779 	Q2_FindFile (modelpath, &file);
2780 	if(file)
2781 	{
2782 		//use model specific gibs
2783 		ent->usegibs = 1;
2784 		sprintf(ent->head, "players/%s/head.md2", playermodel);
2785 		sprintf(ent->body, "players/%s/body.md2", playermodel);
2786 		sprintf(ent->leg, "players/%s/leg.md2", playermodel);
2787 		sprintf(ent->arm, "players/%s/arm.md2", playermodel);
2788 		fclose(file);
2789 	}
2790 
2791 	// fov
2792 	ent->client->ps.fov = atoi(Info_ValueForKey(userinfo, "fov"));
2793 	if (ent->client->ps.fov < 1)
2794 		ent->client->ps.fov = 90;
2795 	else if (ent->client->ps.fov > 160)
2796 		ent->client->ps.fov = 160;
2797 
2798 	// handedness
2799 	s = Info_ValueForKey (userinfo, "hand");
2800 	if (strlen(s))
2801 	{
2802 		ent->client->pers.hand = atoi(s);
2803 	}
2804 
2805 	// save off the userinfo in case we want to check something later
2806 	Q_strncpyz2( ent->client->pers.userinfo, userinfo, sizeof(ent->client->pers.userinfo) );
2807 
2808 }
2809 
ClientChangeSkin(edict_t * ent)2810 void ClientChangeSkin (edict_t *ent)
2811 {
2812 	char *s;
2813 	int  playernum;
2814 	int  i, j, k, copychar;
2815 	char playermodel[MAX_OSPATH] = " ";
2816 	char playerskin[MAX_INFO_STRING] = " ";
2817 	char userinfo[MAX_INFO_STRING];
2818 	char tmp_playername[PLAYERNAME_SIZE];
2819 
2820 	//get the userinfo
2821 	memcpy (userinfo, ent->client->pers.userinfo, sizeof(userinfo));
2822 
2823 	// check for malformed or illegal info strings
2824 	if (!Info_Validate(userinfo))
2825 	{
2826 		if(ent->dmteam == RED_TEAM)
2827 			strcpy (userinfo, "\\name\\badinfo\\skin\\martianenforcer/red");
2828 		else if(ent->dmteam == BLUE_TEAM)
2829 			strcpy (userinfo, "\\name\\badinfo\\skin\\martianenforcer/blue");
2830 		else
2831 			strcpy (userinfo, "\\name\\badinfo\\skin\\martianenforcer/default");
2832 
2833 		ent->s.modelindex3 = gi.modelindex("players/martianenforcer/helmet.md2");
2834 	}
2835 
2836 	// set name
2837 	s = Info_ValueForKey (userinfo, "name");
2838 
2839 	// fix player name if corrupted
2840 	if ( s != NULL && s[0] )
2841 	{
2842 
2843 		Q_strncpyz2( tmp_playername, s, sizeof(tmp_playername ) );
2844 		ValidatePlayerName( tmp_playername, sizeof(tmp_playername ) );
2845 		Q_strncpyz2(ent->client->pers.netname, tmp_playername,
2846 				sizeof(ent->client->pers.netname) );
2847 	}
2848 
2849 	// set skin
2850 	s = Info_ValueForKey (userinfo, "skin");
2851 
2852 	copychar = false;
2853 	strcpy(playerskin, " ");
2854 	strcpy(playermodel, " ");
2855 	j = k = 0;
2856 	for(i = 0; i <= strlen(s) && i < MAX_OSPATH; i++)
2857 	{
2858 		if(copychar){
2859 			playerskin[k] = s[i];
2860 			k++;
2861 		}
2862 		else {
2863 			playermodel[j] = s[i];
2864 			j++;
2865 		}
2866 		if(s[i] == '/')
2867 			copychar = true;
2868 
2869 	}
2870 	playermodel[j] = 0;
2871 
2872 
2873 	if( ent->dmteam == BLUE_TEAM)
2874 	{
2875 		if ( !ent->is_bot )
2876 			safe_bprintf (PRINT_MEDIUM, "Joined Blue Team...\n");
2877 		strcpy(playerskin, "blue");
2878 	}
2879 	else
2880 	{
2881 		if ( !ent->is_bot )
2882 			safe_bprintf (PRINT_MEDIUM, "Joined Red Team...\n");
2883 		strcpy(playerskin, "red");
2884 	}
2885 	if(strlen(playermodel) > 32) //something went wrong, or somebody is being malicious
2886 		strcpy(playermodel, "martianenforcer/");
2887 	strcpy(s, playermodel);
2888 	strcat(s, playerskin);
2889 	Info_SetValueForKey (userinfo, "skin", s);
2890 
2891 	playernum = ent-g_edicts-1;
2892 
2893 	// combine name and skin into a configstring
2894 	gi.configstring (CS_PLAYERSKINS+playernum, va("%s\\%s", ent->client->pers.netname, s) );
2895 
2896 	// fov
2897 	ent->client->ps.fov = atoi(Info_ValueForKey(userinfo, "fov"));
2898 	if (ent->client->ps.fov < 1)
2899 		ent->client->ps.fov = 90;
2900 	else if (ent->client->ps.fov > 160)
2901 		ent->client->ps.fov = 160;
2902 
2903 	// handedness
2904 	s = Info_ValueForKey (userinfo, "hand");
2905 	if (strlen(s))
2906 	{
2907 		ent->client->pers.hand = atoi(s);
2908 	}
2909 
2910 	// save off the userinfo in case we want to check something later
2911 	strncpy (ent->client->pers.userinfo, userinfo, sizeof(ent->client->pers.userinfo)-1);
2912 
2913 }
2914 
2915 /*
2916 ===========
2917 ClientConnect
2918 
2919 Called when a player begins connecting to the server.
2920 The game can refuse entrance to a client by returning false.
2921 If the client is allowed, the connection process will continue
2922 and eventually get to ClientBegin()
2923 Changing levels will NOT cause this to be called again, but
2924 loadgames will.
2925 ============
2926 */
2927 
ClientConnect(edict_t * ent,char * userinfo)2928 qboolean ClientConnect (edict_t *ent, char *userinfo)
2929 {
2930 	char	*value;
2931 	int		i, numspec;
2932 
2933 	// check to see if they are on the banned IP list
2934 	value = Info_ValueForKey (userinfo, "ip");
2935 	if (SV_FilterPacket(value)) {
2936 		Info_SetValueForKey(userinfo, "rejmsg", "Banned.");
2937 		return false;
2938 	}
2939 
2940 	//spectator mode
2941 	// check for a spectator
2942 
2943 	value = Info_ValueForKey (userinfo, "spectator");
2944 
2945 	if ( TEAM_GAME  && strcmp(value, "0") )
2946 	{ // for team game, force spectator off
2947 		Info_SetValueForKey( userinfo, "spectator", "0" );
2948 		ent->client->pers.spectator = 0;
2949 	}
2950 
2951 	if (deathmatch->value && *value && strcmp(value, "0")) {
2952 
2953 		value = Info_ValueForKey( userinfo, "spectator_password" );
2954 
2955 		if (*spectator_password->string &&
2956 			strcmp(spectator_password->string, "none") &&
2957 			strcmp(spectator_password->string, value)) {
2958 			Info_SetValueForKey(userinfo, "rejmsg", "Spectator password required or incorrect.");
2959 			return false;
2960 		}
2961 
2962 		// count spectators
2963 		for (i = numspec = 0; i < g_maxclients->value; i++)
2964 			if (g_edicts[i+1].inuse && g_edicts[i+1].client->pers.spectator)
2965 				numspec++;
2966 
2967 		if (numspec >= maxspectators->value) {
2968 			Info_SetValueForKey(userinfo, "rejmsg", "Server spectator limit is full.");
2969 			return false;
2970 		}
2971 	} else if(!ent->is_bot){
2972 		// check for a password
2973 		value = Info_ValueForKey (userinfo, "password");
2974 		if (*password->string && strcmp(password->string, "none") &&
2975 			strcmp(password->string, value)) {
2976 			Info_SetValueForKey(userinfo, "rejmsg", "Password required or incorrect.");
2977 			return false;
2978 		}
2979 	}
2980 	//end specator mode
2981 
2982 	// they can connect
2983 	ent->client = game.clients + (ent - g_edicts - 1);
2984 
2985 	// if there is already a body waiting for us (a loadgame), just
2986 	// take it, otherwise spawn one from scratch
2987 	if (ent->inuse == false)
2988 	{
2989 		// clear the respawning variables
2990 		InitClientResp (ent->client);
2991 		if (!game.autosaved || !ent->client->pers.weapon)
2992 			InitClientPersistant (ent->client);
2993 	}
2994 
2995 	// for real players in team games, team is to be selected
2996 	ent->dmteam = NO_TEAM;
2997 	ent->teamset = false;
2998 
2999 	ClientUserinfoChanged (ent, userinfo, CONNECT);
3000 
3001 	if (game.maxclients > 1)
3002 		gi.dprintf ("%s connected\n", ent->client->pers.netname);
3003 
3004 	ent->client->pers.connected = true;
3005 
3006 	return true;
3007 }
3008 
3009 /*
3010 ===========
3011 ClientDisconnect
3012 
3013 Called when a player drops from the server.
3014 Will not be called between levels.
3015 ============
3016 */
ClientDisconnect(edict_t * ent)3017 void ClientDisconnect (edict_t *ent)
3018 {
3019 	int	playernum, i;
3020 
3021 	if (!ent->client)
3022 		return;
3023 
3024 	safe_bprintf (PRINT_HIGH, "%s disconnected\n", ent->client->pers.netname);
3025 
3026     if(ctf->value)
3027     { //if carrying flag, don't take it with you! no attacker points.
3028 		CTFDeadDropFlag(ent, NULL);
3029     }
3030 
3031 	DeadDropDeathball(ent);
3032 
3033 	if(ent->deadflag && ent->client->chasetoggle == 1)
3034 		DeathcamRemove(ent, "off");
3035 
3036 	//if in duel mode, we need to bump people down the queue if its the player in game leaving
3037 	if(g_duel->integer) {
3038 		MoveClientsDownQueue(ent);
3039 		if(!ent->client->resp.spectator) {
3040 			for (i = 0; i < g_maxclients->value; i++)  //clear scores if player was in duel
3041 				if(g_edicts[i+1].inuse && g_edicts[i+1].client && !g_edicts[i+1].is_bot)
3042 					g_edicts[i+1].client->resp.score = 0;
3043 		}
3044 	}
3045 	// send effect
3046 	gi.WriteByte (svc_muzzleflash);
3047 	gi.WriteShort (ent-g_edicts);
3048 	gi.WriteByte (MZ_LOGOUT);
3049 	gi.multicast (ent->s.origin, MULTICAST_PVS);
3050 
3051 	gi.unlinkentity (ent);
3052 	ent->s.modelindex = 0;
3053 	ent->s.effects = 0;
3054 	ent->s.sound = 0;
3055 	ent->solid = SOLID_NOT;
3056 	ent->inuse = false;
3057 	ent->classname = "disconnected";
3058 	ent->client->pers.connected = false;
3059 
3060 	playernum = ent-g_edicts-1;
3061 	gi.configstring (CS_PLAYERSKINS+playernum, "");
3062 
3063 	// if using bot thresholds, put the bot back in(duel always uses them)
3064 	if ( sv_botkickthreshold->integer || g_duel->integer )
3065 	{
3066 		ACESP_LoadBots( ent );
3067 	}
3068 
3069 }
3070 
3071 
3072 //==============================================================
3073 
3074 
3075 edict_t	*pm_passent;
3076 
3077 // pmove doesn't need to know about passent and contentmask
PM_trace(vec3_t start,vec3_t mins,vec3_t maxs,vec3_t end)3078 trace_t	PM_trace (vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end)
3079 {
3080 	if (pm_passent->health > 0)
3081 		return gi.trace (start, mins, maxs, end, pm_passent, MASK_PLAYERSOLID);
3082 	else
3083 		return gi.trace (start, mins, maxs, end, pm_passent, MASK_DEADSOLID);
3084 }
3085 
CheckBlock(void * b,int c)3086 unsigned CheckBlock (void *b, int c)
3087 {
3088 	int	v,i;
3089 	v = 0;
3090 	for (i=0 ; i<c ; i++)
3091 		v+= ((byte *)b)[i];
3092 	return v;
3093 }
PrintPmove(pmove_t * pm)3094 void PrintPmove (pmove_t *pm)
3095 {
3096 	unsigned	c1, c2;
3097 
3098 	c1 = CheckBlock (&pm->s, sizeof(pm->s));
3099 	c2 = CheckBlock (&pm->cmd, sizeof(pm->cmd));
3100 	Com_Printf ("sv %3i:%i %i\n", pm->cmd.impulse, c1, c2);
3101 }
3102 
3103 /*
3104 ======
3105  TeamCensus
3106 
3107  tallies up players and bots in game
3108  used by auto bot kick, and auto team selection
3109  implements team balancing rules
3110 ======
3111  */
TeamCensus(teamcensus_t * teamcensus)3112 void TeamCensus( teamcensus_t *teamcensus )
3113 {
3114 	int i;
3115 	int diff;
3116 	int dmteam;
3117 	int red_sum;
3118 	int blue_sum;
3119 
3120 	int real_red = 0;
3121 	int real_blue = 0;
3122 	int bots_red = 0;
3123 	int bots_blue = 0;
3124 	int team_for_real = NO_TEAM;
3125 	int team_for_bot = NO_TEAM;
3126 
3127 	for ( i = 1; i <= game.maxclients; i++ )
3128 	{ // count only clients that have joined teams
3129 		if ( g_edicts[i].inuse )
3130 		{
3131 			dmteam = g_edicts[i].dmteam;
3132 			if ( g_edicts[i].is_bot )
3133 			{
3134 				if ( dmteam == RED_TEAM )
3135 				{
3136 					++bots_red;
3137 				}
3138 				else if ( dmteam == BLUE_TEAM )
3139 				{
3140 					++bots_blue;
3141 				}
3142 			}
3143 			else
3144 			{
3145 				if ( dmteam == RED_TEAM )
3146 				{
3147 					++real_red;
3148 				}
3149 				else if ( dmteam == BLUE_TEAM )
3150 				{
3151 					++real_blue;
3152 				}
3153 			}
3154 		}
3155 	}
3156 	red_sum = real_red + bots_red;
3157 	blue_sum = real_blue + bots_blue;
3158 
3159 	// team balancing rules
3160 	if ( red_sum == blue_sum )
3161 	{ // teams are of equal size
3162 		if ( bots_blue == bots_red )
3163 		{ // with equal number of both real players and bots
3164 			// assign by scoring or random selection
3165 			if ( red_team_score > blue_team_score )
3166 			{ // leader gets the bot
3167 				// real player being a good sport joins the team that is behind
3168 				if ( tca->integer )
3169 				{
3170 					team_for_bot = BLUE_TEAM;
3171 					team_for_real = RED_TEAM;
3172 				}
3173 				else
3174 				{
3175 					team_for_bot = RED_TEAM;
3176 					team_for_real = BLUE_TEAM;
3177 				}
3178 			}
3179 			else if ( blue_team_score > red_team_score )
3180 			{
3181 				if ( tca->integer )
3182 				{
3183 					team_for_bot = RED_TEAM;
3184 					team_for_real = BLUE_TEAM;
3185 				}
3186 				else
3187 				{
3188 					team_for_bot = BLUE_TEAM;
3189 					team_for_real = RED_TEAM;
3190 				}
3191 			}
3192 			else if ( rand() & 1 )
3193 			{
3194 				team_for_real = team_for_bot = RED_TEAM;
3195 			}
3196 			else
3197 			{
3198 				team_for_real = team_for_bot = BLUE_TEAM;
3199 			}
3200 		}
3201 		else if ( bots_blue > bots_red )
3202 		{ // with more blue bots than red
3203 			// put bot on team with fewer bots (red)
3204 			// real player on team with fewer real players (blue)
3205 			team_for_bot = RED_TEAM;
3206 			team_for_real = BLUE_TEAM;
3207 		}
3208 		else
3209 		{ // with more red bots than blue
3210 			// put bot on team with fewer bots (blue)
3211 			// real player on team with fewer real players (red)
3212 			team_for_bot = BLUE_TEAM;
3213 			team_for_real = RED_TEAM;
3214 		}
3215 	}
3216 	else
3217 	{ // teams are of unequal size
3218 		diff = red_sum - blue_sum;
3219 		if ( diff > 1 )
3220 		{ // red is 2 or more larger
3221 			team_for_real = team_for_bot = BLUE_TEAM;
3222 		}
3223 		else if ( diff < -1 )
3224 		{ // blue is 2 or more larger
3225 			team_for_real = team_for_bot = RED_TEAM;
3226 		}
3227 		else if ( real_blue == real_red )
3228 		{ // with equal numbers of real players
3229 			if ( bots_blue > bots_red )
3230 			{ // blue team is larger by 1 bot
3231 				team_for_real = team_for_bot = RED_TEAM;
3232 			}
3233 			else
3234 			{ // red_team is larger by 1 bot
3235 				team_for_real = team_for_bot = BLUE_TEAM;
3236 			}
3237 		}
3238 		else
3239 		{ // with equal numbers of bots
3240 			if ( real_blue > real_red )
3241 			{ // blue team is larger by 1 real player
3242 				team_for_real = team_for_bot = RED_TEAM;
3243 			}
3244 			else
3245 			{ // red team is larger by 1 real player
3246 				team_for_real = team_for_bot = BLUE_TEAM;
3247 			}
3248 		}
3249 	}
3250 
3251 	teamcensus->total = red_sum + blue_sum; //   sum of all
3252 	teamcensus->real = real_red + real_blue; //  sum of real players
3253 	teamcensus->bots = bots_red + bots_blue; //  sum of bots
3254 	teamcensus->red = real_red + bots_red; //    sum of red team members
3255 	teamcensus->blue = real_blue + bots_blue; // sum of blue team members
3256 	teamcensus->real_red = real_red; //   red team players in game
3257 	teamcensus->real_blue = real_blue; // blue team players in game
3258 	teamcensus->bots_red = bots_red; //   red team bots in game
3259 	teamcensus->bots_blue = bots_blue; // blue team bots in game
3260 	teamcensus->team_for_real = team_for_real; // team for real player to join
3261 	teamcensus->team_for_bot = team_for_bot; //   team for bot to join
3262 }
3263 
3264 
3265 /** @brief Handle clients that are in "team selection" mode
3266  *
3267  * @todo this function needs cleaning up - if we set spectator mode to 0, we
3268  *	can return from the function; if that is done, then we no longer need
3269  *	to make sure that the client is not in a team in the rest of the
3270  *	function.
3271  *
3272  * @param ent entity of the client
3273  * @param client the client itself
3274  * @param ucmd user command from the client
3275  */
ClientTeamSelection(edict_t * ent,gclient_t * client,usercmd_t * ucmd)3276 static inline void ClientTeamSelection( edict_t * ent ,
3277 		gclient_t * client ,
3278 		usercmd_t * ucmd )
3279 {
3280 	teamcensus_t teamcensus;
3281 
3282 	if ( client->pers.spectator == 2 )
3283 	{ // entered with special team spectator mode on
3284 		// force it off, does not appear to help much. (?)
3285 		ent->dmteam = NO_TEAM;
3286 		client->pers.spectator = 0;
3287 	}
3288 
3289 	if ( ent->dmteam == RED_TEAM || ent->dmteam == BLUE_TEAM )
3290 	{
3291 		client->pers.spectator = 0;
3292 	}
3293 
3294 	if ( level.time / 2 == ceil( level.time / 2 )
3295 			&& client->pers.spectator == 1
3296 			&& ent->dmteam == NO_TEAM ) {
3297 		// send "how to join" message
3298 		if ( g_autobalance->integer )
3299 		{
3300 			safe_centerprintf( ent,
3301 				"\n\n\nPress <fire> to join\n"
3302 				"autobalanced team\n" );
3303 		}
3304 		else
3305 		{
3306 			safe_centerprintf( ent,
3307 				"\n\n\nPress <fire> to autojoin\n"
3308 				"or <jump> to join BLUE\n"
3309 				"or <crouch> to join RED\n" );
3310 		}
3311 	}
3312 
3313 	if ( client->latched_buttons & BUTTON_ATTACK )
3314 	{ // <fire> to auto join
3315 		client->latched_buttons = 0;
3316 		if ( client->pers.spectator == 1 && ent->dmteam == NO_TEAM)
3317 		{
3318 			TeamCensus( &teamcensus ); // apply team balance rules
3319 			ent->dmteam = teamcensus.team_for_real;
3320 			client->pers.spectator = 0;
3321 			ClientChangeSkin( ent );
3322 		}
3323 	}
3324 
3325 	if ( ucmd->upmove >= 10 && ent->dmteam == NO_TEAM )
3326 	{
3327 		if ( !g_autobalance->integer )
3328 		{ // jump to join blue
3329 			ent->dmteam = BLUE_TEAM;
3330 			client->pers.spectator = 0;
3331 			ClientChangeSkin( ent );
3332 		}
3333 	}
3334 	else if ( ucmd->upmove < 0 && ent->dmteam == NO_TEAM )
3335 	{
3336 		if ( !g_autobalance->integer )
3337 		{ // crouch to join red
3338 			ent->dmteam = RED_TEAM;
3339 			client->pers.spectator = 0;
3340 			ClientChangeSkin( ent );
3341 		}
3342 	}
3343 	else
3344 	{
3345 		client->ps.pmove.pm_flags &= ~PMF_JUMP_HELD;
3346 	}
3347 }
3348 
3349 
3350 /*
3351 ==============
3352 ClientThink
3353 
3354 This will be called once for each client frame, which will
3355 usually be a couple times for each server frame.
3356 ==============
3357 */
ClientThink(edict_t * ent,usercmd_t * ucmd)3358 void ClientThink (edict_t *ent, usercmd_t *ucmd)
3359 {
3360 	gclient_t	*client;
3361 	edict_t	*other;
3362 	int		i, j, mostvotes, n_candidates;
3363 	int		map_candidates[4];
3364 	pmove_t	pm;
3365 	qboolean sproing, haste;
3366 	vec3_t addspeed, forward, up, right;
3367 
3368 	level.current_entity = ent;
3369 	client = ent->client;
3370 
3371 	// If the MOTD is being forced on, decrease frame counter
3372 	// and re-send the file if necessary
3373 	if ( client->motd_frames )
3374 	{
3375 		if ( level.time - floor( level.time ) != 0 )
3376 		{
3377 			SendMessageOfTheDay( ent );
3378 		}
3379 		client->motd_frames --;
3380 	}
3381 
3382 	//unlagged
3383 	if ( g_antilag->integer)
3384 		client->attackTime = gi.Sys_Milliseconds();
3385 
3386 	if (level.intermissiontime)
3387 	{
3388 		client->ps.pmove.pm_type = PM_FREEZE;
3389 
3390 		// can exit intermission after 10 seconds, or 20 if map voting enables
3391 		// (voting will only work if g_mapvote wasn't modified during intermission)
3392 		if (g_mapvote->value && ! g_mapvote->modified)
3393 		{
3394 			//print out results, track winning map
3395 			mostvotes = 0;
3396 
3397 			for (j = 0; j < 4; j++)
3398 			{
3399 				if (votedmap[j].tally > mostvotes)
3400 					mostvotes = votedmap[j].tally;
3401 			}
3402 
3403 			if ( g_voterand && g_voterand->value )
3404 			{
3405 				// we're using a random value for the next map
3406 				// if a choice needs to be done
3407 				n_candidates = 0;
3408 				for ( j = 0 ; j < 4 ; j ++ )
3409 				{
3410 					if ( votedmap[j].tally < mostvotes )
3411 						continue;
3412 					map_candidates[n_candidates ++] = j;
3413 				}
3414 
3415 				j = random() * (n_candidates - 1);
3416 				level.changemap = votedmap[map_candidates[j]].mapname;
3417 			}
3418 			else
3419 			{
3420 				// "old" voting system, take the first map that
3421 				// has enough votes
3422 				for ( j = 0 ; j < 4 ; j ++ )
3423 				{
3424 					i = (j + 1) % 4;
3425 					if ( votedmap[i].tally < mostvotes )
3426 						continue;
3427 				    level.changemap = votedmap[i].mapname;
3428 					break;
3429 				}
3430 			}
3431 
3432 			if (level.time > level.intermissiontime + 20.0
3433 				&& (ucmd->buttons & BUTTON_ANY) )
3434 				level.exitintermission = true;
3435 		}
3436 		else
3437 		{
3438 			if (level.time > level.intermissiontime + 10.0
3439 				&& (ucmd->buttons & BUTTON_ANY) )
3440 				level.exitintermission = true;
3441 		}
3442 		return;
3443 	}
3444 	else if ( g_mapvote && g_mapvote->modified )
3445 	{
3446 		g_mapvote->modified = false;
3447 	}
3448 
3449 	pm_passent = ent;
3450 
3451 	if (ent->client->chase_target)
3452 	{
3453 		client->resp.cmd_angles[0] = SHORT2ANGLE(ucmd->angles[0]);
3454 		client->resp.cmd_angles[1] = SHORT2ANGLE(ucmd->angles[1]);
3455 		client->resp.cmd_angles[2] = SHORT2ANGLE(ucmd->angles[2]);
3456 
3457 	} else
3458 	{
3459 		// set up for pmove
3460 		memset (&pm, 0, sizeof(pm));
3461 
3462 		if (ent->movetype == MOVETYPE_NOCLIP)
3463 			client->ps.pmove.pm_type = PM_SPECTATOR;
3464 		else if (ent->s.modelindex != 255 && !(ent->in_vehicle) && !(client->chasetoggle)) //for vehicles or deathcam
3465 			client->ps.pmove.pm_type = PM_GIB;
3466 		else if (ent->deadflag)
3467 			client->ps.pmove.pm_type = PM_DEAD;
3468 		else
3469 			client->ps.pmove.pm_type = PM_NORMAL;
3470 
3471 		if(!client->chasetoggle)
3472 		{
3473 			client->ps.pmove.gravity = sv_gravity->value;
3474 		}
3475 		else
3476 		{	/* No gravity to move the deathcam */
3477 			client->ps.pmove.gravity = 0;
3478 		}
3479 
3480 		//vehicles
3481 		if ( Jet_Active(ent) )
3482 			Jet_ApplyJet( ent, ucmd );
3483 
3484 		pm.s = client->ps.pmove;
3485 
3486 		for (i=0 ; i<3 ; i++)
3487 		{
3488 			pm.s.origin[i] = ent->s.origin[i]*8;
3489 			pm.s.velocity[i] = ent->velocity[i]*8;
3490 		}
3491 
3492 		if (memcmp(&client->old_pmove, &pm.s, sizeof(pm.s)))
3493 		{
3494 			pm.snapinitial = true;
3495 		}
3496 
3497 		if(g_tactical->integer)
3498 		{
3499 			//limit acceleration
3500 			if(ucmd->forwardmove > 200)
3501 				ucmd->forwardmove = 200;
3502 			if(ucmd->forwardmove < -200)
3503 				ucmd->forwardmove = -200;
3504 			if(ucmd->sidemove > 200)
3505 				ucmd->sidemove = 200;
3506 			if(ucmd->sidemove < -200)
3507 				ucmd->sidemove = -200;
3508 		}
3509 
3510 		ucmd->forwardmove *= 1.3;
3511 
3512 		//tactical
3513 		if(g_tactical->integer)
3514 		{
3515 			client->dodge = false;
3516 		}
3517 		else
3518 		{
3519 			//dodging
3520 			client->dodge = false;
3521 
3522 			if((level.time - client->lastdodge) > 1.0 && ent->groundentity && ucmd->forwardmove == 0 && ucmd->sidemove != 0 && client->moved == false
3523 				&& client->keydown < 10 && ((level.time - client->lastmovetime) < .15))
3524 			{
3525 				if((ucmd->sidemove < 0 && client->lastsidemove < 0) || (ucmd->sidemove > 0 && client->lastsidemove > 0))
3526 				{
3527 					if(ucmd->sidemove > 0)
3528 						client->dodge = 1;
3529 					else
3530 						client->dodge = -1;
3531 					ucmd->upmove += 100;
3532 				}
3533 			}
3534 			if((level.time - client->lastdodge) > 1.0 && ent->groundentity && ucmd->forwardmove != 0 && ucmd->sidemove == 0 && client->moved == false
3535 				&& client->keydown < 10 && ((level.time - client->lastmovetime) < .15))
3536 			{
3537 				if((ucmd->forwardmove < 0 && client->lastforwardmove < 0) || (ucmd->forwardmove > 0 && client->lastforwardmove > 0))
3538 				{
3539 					if(ucmd->forwardmove > 0)
3540 						client->dodge = 2;
3541 					else
3542 						client->dodge = -2;
3543 					ucmd->upmove += 100;
3544 				}
3545 			}
3546 		}
3547 
3548 		//checking previous frame's movement
3549 		if(client->moved == true && (ucmd->buttons & BUTTON_ANY))
3550 		{
3551 			client->keydown++;
3552 		}
3553 		else if (ucmd->sidemove != 0 || ucmd->forwardmove != 0)
3554 		{
3555 			client->keydown = 0;
3556 		}
3557 
3558 		if(ucmd->sidemove != 0 || ucmd->forwardmove != 0)
3559 		{
3560 			client->lastmovetime = level.time;
3561 			client->lastsidemove = ucmd->sidemove;
3562 			client->lastforwardmove = ucmd->forwardmove;
3563 			client->moved = true;
3564 		}
3565 		else //we had a frame with no movement
3566 			client->moved = false;
3567 
3568 		pm.cmd = *ucmd;
3569 
3570 		pm.trace = PM_trace;	// adds default parms
3571 		pm.pointcontents = gi.pointcontents;
3572 
3573 		//joust mode
3574 		if(joustmode->value)
3575 		{
3576 			if(ent->groundentity)
3577 				client->joustattempts = 0;
3578 			if(pm.cmd.upmove >= 10)
3579 			{
3580 				client->joustattempts++;
3581 				pm.joustattempts = client->joustattempts;
3582 				if(pm.joustattempts == 10 || pm.joustattempts == 20)
3583 				{
3584 					gi.sound(ent, CHAN_VOICE, gi.soundindex("*jump1.wav"), 1, ATTN_NORM, 0);
3585 					PlayerNoise(ent, ent->s.origin, PNOISE_SELF);
3586 				}
3587 			}
3588 		}
3589 
3590 		// perform a pmove
3591 		gi.Pmove (&pm);
3592 
3593 		// save results of pmove
3594 		client->ps.pmove = pm.s;
3595 		client->old_pmove = pm.s;
3596 
3597 		for (i=0 ; i<3 ; i++)
3598 		{
3599 			ent->s.origin[i] = pm.s.origin[i]*0.125;
3600 			ent->velocity[i] = pm.s.velocity[i]*0.125;
3601 		}
3602 
3603 		//check for a dodge, and peform if true
3604 		if(client->dodge != 0)
3605 		{
3606 			//check for dodge direction
3607 			if(client->dodge == 2 || client->dodge == -2)
3608 			{
3609 				//was forward or backward
3610 				AngleVectors (ent->s.angles, addspeed, right, up);
3611 				client->dodge /= 2;
3612 			}
3613 			else
3614 			{
3615 				//was sideways
3616 				AngleVectors (ent->s.angles, forward, addspeed, up);
3617 			}
3618 
3619 			addspeed[0] *= 300*client->dodge;
3620 			addspeed[1] *= 300*client->dodge;
3621 
3622 			VectorAdd(ent->velocity, addspeed, ent->velocity);
3623 
3624 			//check velocity
3625 			SV_CheckVelocity(ent);
3626 
3627 			client->dodge = false;
3628 			client->lastdodge = client->lastmovetime = level.time;
3629 		}
3630 
3631 		VectorCopy (pm.mins, ent->mins);
3632 		VectorCopy (pm.maxs, ent->maxs);
3633 
3634 		client->resp.cmd_angles[0] = SHORT2ANGLE(ucmd->angles[0]);
3635 		client->resp.cmd_angles[1] = SHORT2ANGLE(ucmd->angles[1]);
3636 		client->resp.cmd_angles[2] = SHORT2ANGLE(ucmd->angles[2]);
3637 
3638 		//vehicles
3639 		if ( Jet_Active(ent) )
3640 			if( pm.groundentity ) 		/*are we on ground*/
3641 				if ( Jet_AvoidGround(ent) )	/*then lift us if possible*/
3642 					pm.groundentity = NULL;		/*now we are no longer on ground*/
3643 
3644 		if (ent->groundentity && !pm.groundentity && (pm.cmd.upmove >= 10) && (pm.waterlevel == 0))
3645 		{
3646 			sproing = client->sproing_framenum > level.framenum;
3647 			haste = client->haste_framenum > level.framenum;
3648 			if(sproing)
3649 			{
3650 				gi.sound(ent, CHAN_VOICE, gi.soundindex("items/sproing.wav"), 1, ATTN_NORM, 0);
3651 				ent->velocity[2] += 400;
3652 			}
3653 			if(haste && ucmd->forwardmove > 0)
3654 			{
3655 				AngleVectors (ent->s.angles, addspeed, right, up);
3656 				addspeed[0] *= 400;
3657 				addspeed[1] *= 400;
3658 				for(i = 0; i < 2; i++)
3659 				{
3660 					if(addspeed[i] > 200)
3661 						addspeed[i] = 200;
3662 				}
3663 				VectorAdd(ent->velocity, addspeed, ent->velocity);
3664 
3665 				gi.sound(ent, CHAN_VOICE, gi.soundindex("items/haste.wav"), 1, ATTN_NORM, 0);
3666 				gi.WriteByte (svc_temp_entity);
3667 				gi.WriteByte (TE_EXPLOSION2);
3668 				gi.WritePosition (ent->s.origin);
3669 				gi.multicast (ent->s.origin, MULTICAST_PVS);
3670 			}
3671 			else
3672 				gi.sound(ent, CHAN_VOICE, gi.soundindex("*jump1.wav"), 1, ATTN_NORM, 0);
3673 			PlayerNoise(ent, ent->s.origin, PNOISE_SELF);
3674 		}
3675 
3676 		ent->viewheight = pm.viewheight;
3677 		ent->waterlevel = pm.waterlevel;
3678 		ent->watertype = pm.watertype;
3679 		ent->groundentity = pm.groundentity;
3680 		if (pm.groundentity)
3681 			ent->groundentity_linkcount = pm.groundentity->linkcount;
3682 
3683 		if (ent->deadflag)
3684 		{
3685 			client->ps.viewangles[ROLL] = 40;
3686 			client->ps.viewangles[PITCH] = -15;
3687 			client->ps.viewangles[YAW] = client->killer_yaw;
3688 		}
3689 		else
3690 		{
3691 			VectorCopy (pm.viewangles, client->v_angle);
3692 			VectorCopy (pm.viewangles, client->ps.viewangles);
3693 		}
3694 
3695 		if (client->ctf_grapple)
3696 			CTFGrapplePull(client->ctf_grapple);
3697 
3698 		gi.linkentity (ent);
3699 
3700 		if (ent->movetype != MOVETYPE_NOCLIP)
3701 			G_TouchTriggers (ent);
3702 
3703 		// touch other objects
3704 		for (i=0 ; i<pm.numtouch ; i++)
3705 		{
3706 			other = pm.touchents[i];
3707 			for (j=0 ; j<i ; j++)
3708 				if (pm.touchents[j] == other)
3709 					break;
3710 			if (j != i)
3711 				continue;	// duplicated
3712 			if (!other->touch)
3713 				continue;
3714 			other->touch (other, ent, NULL, NULL);
3715 		}
3716 	}
3717 
3718 	client->oldbuttons = client->buttons;
3719 	client->buttons = ucmd->buttons;
3720 	client->latched_buttons |= client->buttons & ~client->oldbuttons;
3721 
3722 	// save light level the player is standing on for
3723 	// monster sighting AI
3724 	ent->light_level = ucmd->lightlevel;
3725 
3726 	if ( client->resp.spectator == 0 )
3727 	{ // regular (non-spectator) mode
3728 		if (client->latched_buttons & BUTTON_ATTACK)
3729 		{
3730 			if (!client->weapon_thunk)
3731 			{
3732 				client->weapon_thunk = true;
3733 				Think_Weapon (ent);
3734 			}
3735 		}
3736 	}
3737 	else
3738 	{ // spectator mode
3739 		if ( TEAM_GAME && client->resp.spectator < 2 )
3740 		{ // team selection state
3741 			ClientTeamSelection( ent , client , ucmd );
3742 		} /* team selection state */
3743 		else
3744 		{ // regular spectator
3745 			if ( client->latched_buttons & BUTTON_ATTACK )
3746 			{
3747 				client->latched_buttons = 0;
3748 				if ( client->chase_target )
3749 				{
3750 					client->chase_target = NULL;
3751 					client->ps.pmove.pm_flags &= ~PMF_NO_PREDICTION;
3752 				}
3753 				else
3754 				{
3755 					GetChaseTarget( ent );
3756 				}
3757 			}
3758 			if ( ucmd->upmove >= 10 )
3759 			{
3760 				if ( !(client->ps.pmove.pm_flags & PMF_JUMP_HELD) )
3761 				{
3762 					client->ps.pmove.pm_flags |= PMF_JUMP_HELD;
3763 					if ( client->chase_target )
3764 						ChaseNext( ent );
3765 					else
3766 						GetChaseTarget( ent );
3767 				}
3768 			}
3769 			else
3770 			{
3771 				client->ps.pmove.pm_flags &= ~PMF_JUMP_HELD;
3772 			}
3773 		} /* regular spectator */
3774 	} /* spectator mode */
3775 
3776 	// update chase cam if being followed
3777 	for (i = 1; i <= g_maxclients->value; i++)
3778 	{
3779 		other = g_edicts + i;
3780 		if (other->inuse && other->client->chase_target == ent)
3781 			UpdateChaseCam(other);
3782 	}
3783 
3784 	//mutators
3785 	if((regeneration->value || excessive->value) && !ent->deadflag)
3786 	{
3787 		if((ent->health < ent->max_health) && (client->regen_framenum < level.framenum))
3788 		{
3789 			client->regen_framenum = level.framenum + 5;
3790 			ent->health+=2;
3791 		}
3792 	}
3793 
3794 	//spawn protection has run out
3795 	if(level.time > ent->client->spawnprotecttime + g_spawnprotect->integer)
3796 		ent->client->spawnprotected = false;
3797 
3798 	//lose one health every second
3799 	if(g_losehealth->value && !ent->deadflag)
3800 	{
3801 		if(regeneration->value || excessive->value || vampire->value)
3802 			return;
3803 		if((ent->health > g_losehealth_num->value) && (client->losehealth_framenum < level.framenum))
3804 		{
3805 			client->losehealth_framenum = level.framenum + 10;
3806 			ent->health-=1;
3807 		}
3808 	}
3809 }
3810 
3811 
3812 /** @brief Update the anti-camp timeout
3813  *
3814  * The anti-camp computation accumulates players' velocities, averages them
3815  * and updates the "suicide_timeout" field if the length of that average
3816  * vector is above some threshold.
3817  *
3818  * It is controlled by the camptime, ac_frames and ac_threshold CVars.
3819  */
_UpdateAntiCamp(edict_t * ent)3820 static inline void _UpdateAntiCamp( edict_t * ent )
3821 {
3822 	int n_frames = ac_frames->integer;
3823 	float thresh;
3824 	vec3_t avg;
3825 
3826 	// Make sure we have a valid ac_frames
3827 	if ( n_frames < 1 || n_frames > 100 ) {
3828 		n_frames = G_ANTICAMP_FRAMES;
3829 	}
3830 
3831 	if ( ent->old_velocities_count < n_frames ) {
3832 		// Not enough frames yet
3833 		ent->old_velocities_count ++;
3834 		ent->old_velocities_current ++;
3835 	} else {
3836 		// Enough frames - remove oldest known velocity from
3837 		// accumulator
3838 		ent->old_velocities_current = ( ent->old_velocities_current
3839 				+ 1 ) % n_frames;
3840 		VectorSubtract( ent->velocity_accum ,
3841 			ent->old_velocities[ ent->old_velocities_current ] ,
3842 			ent->velocity_accum );
3843 	}
3844 
3845 	// Store current velocity into history and add its value to the
3846 	// accumulator
3847 	VectorCopy( ent->velocity ,
3848 			ent->old_velocities[ ent->old_velocities_current ] );
3849 	VectorAdd( ent->velocity_accum , ent->velocity , ent->velocity_accum );
3850 	if ( ent->old_velocities_count < n_frames ) {
3851 		return;
3852 	}
3853 
3854 	// Get and adjust speed threshold
3855 	thresh = ac_threshold->value;
3856 	if ( thresh <= 0 || thresh > 500 ) {
3857 		thresh = G_ANTICAMP_THRESHOLD;
3858 	}
3859 	if ( excessive->integer ) {
3860 		thresh *= 1.5;
3861 	}
3862 
3863 	// Check average velocity lengths against threshold
3864 	VectorCopy( ent->velocity_accum , avg );
3865 	avg[0] /= n_frames; avg[1] /= n_frames; avg[2] /= n_frames;
3866 	if ( VectorLength( avg ) > thresh ) {
3867 		ent->suicide_timeout = level.time + camptime->integer;
3868 	}
3869 
3870 	// Inflict anti-camp damage
3871 	if ( ent->suicide_timeout < level.time && ent->takedamage == DAMAGE_AIM
3872 			&& ! ent->client->resp.spectator) {
3873 		T_Damage (ent, world, world, vec3_origin, ent->s.origin,
3874 				vec3_origin, ent->dmg, 0, DAMAGE_NO_ARMOR,
3875 				MOD_SUICIDE);
3876 		safe_centerprintf(ent, "Anticamp: move or die!\n");
3877 	}
3878 }
3879 
3880 
3881 /*
3882 ==============
3883 ClientBeginServerFrame
3884 
3885 This will be called once for each server frame, before running
3886 any other entities in the world.
3887 ==============
3888 */
ClientBeginServerFrame(edict_t * ent)3889 void ClientBeginServerFrame (edict_t *ent)
3890 {
3891 	gclient_t	*client;
3892 	int			buttonMask;
3893 
3894 	if (level.intermissiontime)
3895 		return;
3896 
3897 	client = ent->client;
3898 
3899 	//spectator mode
3900 	if ( deathmatch->value && client->pers.spectator != client->resp.spectator
3901 			&& (level.time - client->respawn_time) >= 5 )
3902 	{
3903 		if ( TEAM_GAME && client->pers.spectator == 2 )
3904 		{ // special team game spectator mode (has problems)
3905 			if ( client->resp.spectator == 1 )
3906 			{ // special team spectator mode
3907 				spectator_respawn( ent );
3908 				client->resp.spectator = 2;
3909 			}
3910 		}
3911 		else if ( TEAM_GAME && client->resp.spectator == 2 )
3912 		{ // pers != resp, exit spectator mode not allowed
3913 			client->pers.spectator = 2;
3914 		}
3915 		else
3916 		{ // enter or exit spectator mode
3917 			spectator_respawn( ent );
3918 		}
3919 		return;
3920 	}
3921 	//end spectator mode
3922 
3923 	//anti-camp
3924 	// do not apply to godmode cheat or bots.
3925 	// bots have other suicidal tendencies which may (or may not) conflict.
3926 	if ( anticamp->integer && !(ent->flags & FL_GODMODE) && !(ent->is_bot) )
3927 	{
3928 		_UpdateAntiCamp( ent );
3929 	}
3930 
3931 	//spectator mode
3932 
3933 	if (!client->weapon_thunk && !client->resp.spectator)
3934 	//end spectator mode
3935 
3936 		Think_Weapon (ent);
3937 	else
3938 		client->weapon_thunk = false;
3939 
3940 	if (ent->deadflag)
3941 	{
3942 		// wait for any button just going down
3943 		if ( level.time > client->respawn_time)
3944 		{
3945 			// in deathmatch, only wait for attack button
3946 			if (deathmatch->value)
3947 				buttonMask = BUTTON_ATTACK | BUTTON_ATTACK2;
3948 			else
3949 				buttonMask = -1;
3950 
3951 			//should probably add in a force respawn option
3952 			if (( client->latched_buttons & buttonMask ) ||
3953 				(deathmatch->value && (dmflags->integer & DF_FORCE_RESPAWN) ) )
3954 			{
3955 
3956 				if(!ent->is_bot)
3957 					DeathcamRemove (ent, "off");
3958 
3959 				respawn(ent);
3960 				client->latched_buttons = 0;
3961 			}
3962 		}
3963 		return;
3964 	}
3965 
3966 	// add player trail so monsters can follow
3967 	if (!deathmatch->value)
3968 		if (!visible (ent, PlayerTrail_LastSpot() ) )
3969 			PlayerTrail_Add (ent->s.old_origin);
3970 
3971 	client->latched_buttons = 0;
3972 }
3973