1 #include "g_local.h"
2 #include "m_player.h"
3 
4 #define KICK_DELAY_TIME 2			//server frames (.1 sec each)
5 #define DEFAULT_MOTD_TIME 15		//seconds itll remain
6 #define MOTDMAX 26
7 
8 void ClientUserinfoChanged (edict_t *ent, char *userinfo);
9 void CopyToBodyQue (edict_t *ent);
10 void SP_misc_teleporter_dest (edict_t *ent);
11 edict_t * MakeNewBody (edict_t *ent);
12 edict_t * ThrowClientHeadNew (edict_t *self);
13 
14 
15 //
16 // Gross, ugly, disgustuing hack section
17 //
18 
19 // this function is an ugly as hell hack to fix some map flaws
20 //
21 // the coop spawn spots on some maps are SNAFU.  There are coop spots
22 // with the wrong targetname as well as spots with no name at all
23 //
24 // we use carnal knowledge of the maps to fix the coop spot targetnames to match
25 // that of the nearest named single player spot
26 
SP_FixCoopSpots(edict_t * self)27 static void SP_FixCoopSpots (edict_t *self)
28 {
29 	edict_t	*spot;
30 	vec3_t	d;
31 
32 	spot = NULL;
33 
34 	while(1)
35 	{
36 		spot = G_Find(spot, FOFS(classname), "info_player_start");
37 		if (!spot)
38 			return;
39 		if (!spot->targetname)
40 			continue;
41 		VectorSubtract(self->s.origin, spot->s.origin, d);
42 		if (VectorLength(d) < 384)
43 		{
44 			if ((!self->targetname) || Q_stricmp(self->targetname, spot->targetname) != 0)
45 			{
46 //				gi.dprintf("FixCoopSpots changed %s at %s targetname from %s to %s\n", self->classname, vtos(self->s.origin), self->targetname, spot->targetname);
47 				self->targetname = spot->targetname;
48 			}
49 			return;
50 		}
51 	}
52 }
53 
54 // now if that one wasn't ugly enough for you then try this one on for size
55 // some maps don't have any coop spots at all, so we need to create them
56 // where they should have been
57 
SP_CreateCoopSpots(edict_t * self)58 static void SP_CreateCoopSpots (edict_t *self)
59 {
60 	edict_t	*spot;
61 
62 	if(Q_stricmp(level.mapname, "security") == 0)
63 	{
64 		spot = G_Spawn();
65 		spot->classname = "info_player_coop";
66 		spot->s.origin[0] = 188 - 64;
67 		spot->s.origin[1] = -164;
68 		spot->s.origin[2] = 80;
69 		spot->targetname = "jail3";
70 		spot->s.angles[1] = 90;
71 
72 		spot = G_Spawn();
73 		spot->classname = "info_player_coop";
74 		spot->s.origin[0] = 188 + 64;
75 		spot->s.origin[1] = -164;
76 		spot->s.origin[2] = 80;
77 		spot->targetname = "jail3";
78 		spot->s.angles[1] = 90;
79 
80 		spot = G_Spawn();
81 		spot->classname = "info_player_coop";
82 		spot->s.origin[0] = 188 + 128;
83 		spot->s.origin[1] = -164;
84 		spot->s.origin[2] = 80;
85 		spot->targetname = "jail3";
86 		spot->s.angles[1] = 90;
87 
88 		return;
89 	}
90 }
91 
92 //void SP_LensFlare (edict_t *flare)
93 //void SP_SunFlare (edict_t *flare)
94 
SP_AddSunFlares(edict_t * self)95 static void SP_AddSunFlares (edict_t *self)
96 {
97 	edict_t	*newent;
98 
99 	if(Q_stricmp(level.mapname, "base1") == 0)
100 	{
101 		newent = G_Spawn();
102 		newent->s.origin[0] = -155;
103 		newent->s.origin[1] = 877;
104 		newent->s.origin[2] = 441;
105 		SP_SunFlare (newent);
106 		return;
107 	}
108 }
109 
110 /*QUAKED info_player_start (1 0 0) (-16 -16 -24) (16 16 32)
111 The normal starting point for a level.
112 */
SP_info_player_start(edict_t * self)113 void SP_info_player_start(edict_t *self)
114 {
115 	if (!coop->value && !deathmatch->value)
116 	{
117 		self->think = SP_AddSunFlares;
118 		self->nextthink = level.time + FRAMETIME;
119 		return;
120 	}
121 	if (!coop->value)
122 		return;
123 	if(Q_stricmp(level.mapname, "security") == 0)
124 	{
125 		// invoke one of our gross, ugly, disgusting hacks
126 		self->think = SP_CreateCoopSpots;
127 		self->nextthink = level.time + FRAMETIME;
128 	}
129 }
130 
131 /*QUAKED info_player_deathmatch (1 0 1) (-16 -16 -24) (16 16 32)
132 potential spawning position for deathmatch games
133 */
SP_info_player_deathmatch(edict_t * self)134 void SP_info_player_deathmatch(edict_t *self)
135 {
136 	if (!deathmatch->value)
137 	{
138 		G_FreeEdict (self);
139 		return;
140 	}
141 	SP_misc_spawn_dest (self);
142 }
143 
144 /*QUAKED info_player_coop (1 0 1) (-16 -16 -24) (16 16 32)
145 potential spawning position for coop games
146 */
147 
SP_info_player_coop(edict_t * self)148 void SP_info_player_coop(edict_t *self)
149 {
150 	if (!coop->value)
151 	{
152 		G_FreeEdict (self);
153 		return;
154 	}
155 
156 	if((Q_stricmp(level.mapname, "jail2") == 0)   ||
157 	   (Q_stricmp(level.mapname, "jail4") == 0)   ||
158 	   (Q_stricmp(level.mapname, "mine1") == 0)   ||
159 	   (Q_stricmp(level.mapname, "mine2") == 0)   ||
160 	   (Q_stricmp(level.mapname, "mine3") == 0)   ||
161 	   (Q_stricmp(level.mapname, "mine4") == 0)   ||
162 	   (Q_stricmp(level.mapname, "lab") == 0)     ||
163 	   (Q_stricmp(level.mapname, "boss1") == 0)   ||
164 	   (Q_stricmp(level.mapname, "fact3") == 0)   ||
165 	   (Q_stricmp(level.mapname, "biggun") == 0)  ||
166 	   (Q_stricmp(level.mapname, "space") == 0)   ||
167 	   (Q_stricmp(level.mapname, "command") == 0) ||
168 	   (Q_stricmp(level.mapname, "power2") == 0) ||
169 	   (Q_stricmp(level.mapname, "strike") == 0))
170 	{
171 		// invoke one of our gross, ugly, disgusting hacks
172 		self->think = SP_FixCoopSpots;
173 		self->nextthink = level.time + FRAMETIME;
174 	}
175 }
176 
177 
178 /*QUAKED info_player_intermission (1 0 1) (-16 -16 -24) (16 16 32)
179 The deathmatch intermission point will be at one of these
180 Use 'angles' instead of 'angle', so you can set pitch or roll as well as yaw.  'pitch yaw roll'
181 */
SP_info_player_intermission(void)182 void SP_info_player_intermission(void)
183 {
184 }
185 
186 
187 //=======================================================================
188 
189 
player_pain(edict_t * self,edict_t * other,float kick,int damage)190 void player_pain (edict_t *self, edict_t *other, float kick, int damage)
191 {
192 	// player pain is handled at the end of the frame in P_DamageFeedback
193 }
194 
195 
IsFemale(edict_t * ent)196 qboolean IsFemale (edict_t *ent)
197 {
198 	char		*info;
199 
200 	if (!ent->client)
201 		return false;
202 
203 	info = Info_ValueForKey (ent->client->pers.userinfo, "gender");
204 	if (info[0] == 'f' || info[0] == 'F')
205 		return true;
206 	return false;
207 }
208 
IsNeutral(edict_t * ent)209 qboolean IsNeutral (edict_t *ent)
210 {
211 	char		*info;
212 
213 	if (!ent->client)
214 		return false;
215 
216 	info = Info_ValueForKey (ent->client->pers.userinfo, "gender");
217 	if (info[0] != 'f' && info[0] != 'F' && info[0] != 'm' && info[0] != 'M')
218 		return true;
219 	return false;
220 }
221 
ClientObituary(edict_t * self,edict_t * inflictor,edict_t * attacker)222 void ClientObituary (edict_t *self, edict_t *inflictor, edict_t *attacker)
223 {
224 	int			mod;
225 	char		*message;
226 	char		*message2;
227 	qboolean	ff, headshot;
228 
229 	if (coop->value && attacker->client)
230 		meansOfDeath |= MOD_FRIENDLY_FIRE;
231 
232 	if (deathmatch->value || coop->value || true)
233 	{
234 		ff = meansOfDeath & MOD_FRIENDLY_FIRE;
235 		mod = meansOfDeath & ~MOD_FRIENDLY_FIRE & ~MOD_HEAD;
236 		message = NULL;
237 		message2 = "";
238 /*
239 		if (headShot)
240 			gi.centerprintf (self, make_green("(HeadshoT)"));
241 		else
242 			gi.centerprintf (self, make_green("(BodyshoT)"));//*/
243 
244 		switch (mod)
245 		{
246 		case MOD_SUICIDE:
247 			message = "suicides";
248 			break;
249 		case MOD_FALLING:
250 			message = "cratered";
251 			break;
252 		case MOD_CRUSH:
253 			message = "was squished";
254 			break;
255 		case MOD_WATER:
256 			message = "sank like a rock";
257 			break;
258 		case MOD_SLIME:
259 			message = "melted";
260 			break;
261 		case MOD_LAVA:
262 			message = "does a back flip into the lava";
263 			break;
264 		case MOD_EXPLOSIVE:
265 		case MOD_BARREL:
266 			message = "blew up";
267 			break;
268 		case MOD_FLAME:
269 			message = "was torched";
270 			break;
271 		case MOD_EXIT:
272 			message = "found a way out";
273 			break;
274 		case MOD_TARGET_LASER:
275 			message = "saw the light";
276 			break;
277 		case MOD_DISCHARGE:
278 			message = "felt the discharge";
279 			break;
280 		case MOD_TARGET_BLASTER:
281 			message = "got blasted";
282 			break;
283 		case MOD_BOMB:
284 		case MOD_SPLASH:
285 		case MOD_TRIGGER_HURT:
286 			message = "was in the wrong place";
287 			break;
288 		}
289 		if (attacker == self)
290 		{
291 			switch (mod)
292 			{
293 			case MOD_HELD_GRENADE:
294 				message = "tried to put the pin back in";
295 				break;
296 			case MOD_FIRE:
297 				if (IsNeutral(self))
298 					message = "set itself aflame";
299 				else if (IsFemale(self))
300 					message = "set herself aflame";
301 				else
302 					message = "set himself aflame";
303 				break;
304 			case MOD_HG_SPLASH:
305 			case MOD_G_SPLASH:
306 				if (IsNeutral(self))
307 					message = "tripped on its own grenade";
308 				else if (IsFemale(self))
309 					message = "tripped on her own grenade";
310 				else
311 					message = "tripped on his own grenade";
312 				break;
313 			case MOD_R_SPLASH:
314 				if (IsNeutral(self))
315 					message = "blew itself up";
316 				else if (IsFemale(self))
317 					message = "blew herself up";
318 				else
319 					message = "blew himself up";
320 				break;
321 			case MOD_BFG_BLAST:
322 				message = "should have used a smaller gun";
323 				break;
324 			default:
325 				if (IsNeutral(self))
326 					message = "killed itself";
327 				else if (IsFemale(self))
328 					message = "killed herself";
329 				else
330 					message = "killed himself";
331 				break;
332 			}
333 		}
334 		if (message)
335 		{
336 			gi.bprintf (PRINT_MEDIUM, "%s %s.\n", make_white(self->client->pers.netname), message);
337 			if (deathmatch->value)
338 				self->client->resp.score--;
339 			self->enemy = NULL;
340 			return;
341 		}
342 
343 		self->enemy = attacker;
344 		if (attacker && (attacker->client || attacker->svflags & SVF_MONSTER))
345 		{
346 			if (	(headShot)		&&
347 			(mod==MOD_BLASTER2	||mod==MOD_SHOTGUN2		||
348 			 mod==MOD_SSHOTGUN2	||mod==MOD_PUNCH		||
349 			 mod==MOD_CHAINGUN2	||mod==MOD_ROCKET2		||
350 			 mod==MOD_BLASTER	||mod==MOD_SHOTGUN		||
351 			 mod==MOD_SSHOTGUN	||mod==MOD_MACHINEGUN	||
352 			 mod==MOD_CHAINGUN	||mod==MOD_ROCKET		||
353 			 mod==MOD_FIRE		||mod==MOD_HYPERBLASTER	||
354 			 mod==MOD_RAILGUN	||mod==MOD_GRAPPLE		||
355 			 mod==MOD_KICK		||mod==MOD_MAGIC		||
356 			 mod==MOD_SMACK		))
357 			{
358 				switch (mod)
359 				{
360 					case MOD_BLASTER:
361 						message = "'s head was split open by";
362 						message2 = "'s .50 Calibre Handgun";
363 						break;
364 					case MOD_SHOTGUN:
365 						message = "was shot in the head by";
366 						message2 = "'s AutoShotgun";
367 						break;
368 					case MOD_SSHOTGUN:
369 						message = "was shot in the head by";
370 						message2 = "'s 12 Gauge Shotgun";
371 						break;
372 					case MOD_MACHINEGUN:
373 						message = "was shot in the head by";
374 						message2 = "'s SubMachinegun";
375 						break;
376 					case MOD_CHAINGUN:
377 						message = "'s head was mutilated by";
378 						message2 = "'s Chaingun";
379 						break;
380 					case MOD_ROCKET:
381 						message = "had their head split open courtesy of";
382 						message2 = "'s Rocket";
383 						break;
384 					case MOD_BLASTER2:
385 						message = "'s head was split open by";
386 						message2 = "'s Laser Blaster";
387 						break;
388 					case MOD_SHOTGUN2:
389 						message = "was shot in the head by";
390 						message2 = "'s Autocannon";
391 						break;
392 					case MOD_SSHOTGUN2:
393 						message = "was shot in the head by";
394 						message2 = "'s .50 Calibre HandCannon";
395 						break;
396 					case MOD_CHAINGUN2:
397 						message = "'s head was mutilated by";
398 						message2 = "'s Heavy AutoShotgun";
399 						break;
400 					case MOD_ROCKET2:
401 						message = "had their head split open courtesy of";
402 						message2 = "'s Indirect Fire Rocket";
403 						break;
404 					case MOD_HYPERBLASTER:
405 						message = "'s head was melted by";
406 						message2 = "'s Flamethrower";
407 						break;
408 					case MOD_RAILGUN:
409 						message = "took";
410 						message2 =(IsNeutral(self))? "'s Uranium shell through its head":
411 									(IsFemale(self))? "'s Uranium shell through her head":
412 									"'s Uranium shell through his head";
413 						break;
414 					case MOD_GRAPPLE:
415 						message = "'s head was torn open by";
416 						message2 = "'s grappling hook";
417 						break;
418 					case MOD_FIRE:
419 						message = "'s head was charred beyond recognition by";
420 						message2 = "'s pyrotechnics";
421 						break;
422 					case MOD_KICK:
423 						message = "'s head was smashed open by";
424 						message2 = "'s foot";
425 						break;
426 					case MOD_PUNCH:
427 						message = "'s head was smashed open by";
428 						message2 = "'s iron fist";
429 						break;
430 					case MOD_MAGIC:
431 						message = "'s head was ripped apart by";
432 						message2 = (attacker->client)? "'s Magic(tm)" : "'s Turret";
433 						break;
434 					case MOD_SMACK:
435 						message = "'s head smashed open by";
436 						message2 = "'s Pistol";
437 						break;
438 					default:
439 						break;
440 				}
441 			}
442 			else
443 			{
444 				switch (mod)
445 				{
446 					case MOD_HIT:
447 						message = "was beat to death by";
448 						break;
449 					case MOD_BLASTER:
450 						message = "was shot down by";
451 						message2 = "'s .50 caliber handgun";
452 						break;
453 					case MOD_SHOTGUN:
454 						message = "was gunned down by";
455 						break;
456 					case MOD_SSHOTGUN:
457 						message = "was filled with lead by";
458 						message2 = "'s 12 Gauge shotgun";
459 						break;
460 					case MOD_MACHINEGUN:
461 						message = "was machinegunned by";
462 						break;
463 					case MOD_CHAINGUN:
464 						message = "was torn apart by";
465 						message2 = "'s chaingun";
466 						break;
467 					case MOD_GRENADE:
468 						message = "was popped by";
469 						message2 = "'s grenade";
470 						break;
471 					case MOD_G_SPLASH:
472 						message = "was shredded by";
473 						message2 = "'s shrapnel";
474 						break;
475 					case MOD_ROCKET:
476 						message = "ate";
477 						message2 = "'s rocket";
478 						break;
479 					case MOD_BLASTER2:
480 						message = "was shot down by";
481 						message2 = "'s flare-cased Shell";
482 						break;
483 					case MOD_SHOTGUN2:
484 						message = "was blown away by";
485 						break;
486 					case MOD_SSHOTGUN2:
487 						message = "was filled with lead by";
488 						message2 = "'s .50 Calibre HandCannon";
489 						break;
490 					case MOD_CHAINGUN2:
491 						message = "was torn apart by";
492 						message2 = "'s Heavy AutoShotgun";
493 						break;
494 					case MOD_ROCKET2:
495 						message = "ate";
496 						message2 = "'s Indirect-fire Rocket";
497 						break;
498 					case MOD_R_SPLASH:
499 						message = "tried to dodge";
500 						message2 = "'s rocket";
501 						break;
502 					case MOD_HYPERBLASTER:
503 						message = "was torched by";
504 						break;
505 					case MOD_RAILGUN:
506 						message = "took";
507 						message2 =(IsNeutral(self))? "'s Uranium shell through its body":
508 									(IsFemale(self))? "'s Uranium shell through her body":
509 									"'s Uranium shell through his body";
510 						break;
511 					case MOD_BFG_LASER:
512 						message = "was melted by";
513 						message2 = "'s Heavy Laser";
514 						break;
515 					case MOD_BFG_BLAST:
516 						message = "was disintegrated by";
517 						message2 = "'s BFG blast";
518 						break;
519 					case MOD_BFG_EFFECT:
520 						message = "couldn't hide from";
521 						message2 = "'s BFG";
522 						break;
523 					case MOD_ROCKET_BFG:
524 						message = "caught";
525 						message2 = "'s BFG Rocket";
526 						break;
527 					case MOD_R_BFG_SPLASH:
528 						message = "couldn't hide from";
529 						message2 = "'s BFG Rocket";
530 						break;
531 					case MOD_HANDGRENADE:
532 						message = "caught";
533 						message2 = "'s handgrenade";
534 						break;
535 					case MOD_HG_SPLASH:
536 						message = "didn't see";
537 						message2 = "'s handgrenade";
538 						break;
539 					case MOD_GRAPPLE:
540 						message = "was torn apart by";
541 						message2 = "'s grappling hook";
542 						break;
543 					case MOD_C4:
544 						message = "was liquidated by";
545 						message2 = "'s slab of c4";
546 						break;
547 					case MOD_HELD_GRENADE:
548 						message = "was grenaded by";
549 						message2 = "'s grenade";
550 						break;
551 					case MOD_GREN_HIT:
552 						message = "was smacked hard by";
553 						message2 = "'s grenade";
554 						break;
555 					case MOD_FIRE:
556 						message = "was incinerated by";
557 						break;
558 					case MOD_KICK:
559 						message = " had a dose of";
560 						message2 = "'s lethal roundhouse";
561 						break;
562 					case MOD_PUNCH:
563 						message = " was beat down by";
564 						break;
565 					case MOD_MAGIC:
566 						message = "was killed by";
567 						message2 = (attacker->client)? "'s Magic(tm)" : "'s Turret";
568 						break;
569 					case MOD_GASGREN:
570 						message = "was suffocated by";
571 						message2 = "'s Mustard Gas";
572 						break;
573 					case MOD_SMACK:
574 						message = "was beat down by";
575 						message2 = "'s Pistol";
576 						break;
577 					case MOD_GAS_BOOM:
578 						message = "went to hell courtesy of";
579 						break;
580 					case MOD_TAZER:
581 						message = "was electrocuted by";
582 						break;
583 					case MOD_TELEFRAG:
584 						message = "was telefragged by";
585 						//message = "tried to invade";
586 						//message2 = "'s personal space";
587 						break;
588 					default:
589 						break;
590 				}
591 			}
592 
593 			if (message)
594 			{
595 				gi.bprintf (PRINT_MEDIUM,"%s %s %s%s\n", make_green(self->client->pers.netname), message,
596 					make_white( (attacker->client)? attacker->client->pers.netname :
597 						(!Q_stricmp (attacker->classname, "monster_berserk"))? "a Berserker" :
598 						(!Q_stricmp (attacker->classname, "monster_gladiator"))? "a Gladiator" :
599 						(!Q_stricmp (attacker->classname, "monster_gunner"))? "a Pyro Gunner" :
600 						(!Q_stricmp (attacker->classname, "monster_infantry"))? "an Elite Infantryman" :
601 						(!Q_stricmp (attacker->classname, "monster_soldier_light"))? "a Light Soldier" :
602 						(!Q_stricmp (attacker->classname, "monster_soldier"))? "a Soldier" :
603 						(!Q_stricmp (attacker->classname, "monster_soldier_ss"))? "an Elite Soldier" :
604 						(!Q_stricmp (attacker->classname, "turret_driver"))? "a Gunner Infantryman" :
605 						(!Q_stricmp (attacker->classname, "monster_medic"))? "a Medic" :
606 						(!Q_stricmp (attacker->classname, "monster_chick"))? "a Strogg Cyber-Female" :
607 						(!Q_stricmp (attacker->classname, "monster_brain"))? "a Guardian" :
608 						(!Q_stricmp (attacker->classname, "monster_mutant"))? "a Mutant" :
609 						(!Q_stricmp (attacker->classname, "monster_flyer"))? "a Sentinel" :
610 						(!Q_stricmp (attacker->classname, "monster_floater"))? "a Brain" :
611 						(!Q_stricmp (attacker->classname, "monster_flipper"))? "a Pirhanna" :
612 						(!Q_stricmp (attacker->classname, "monster_hover"))? "an Air Gunner" :
613 						(!Q_stricmp (attacker->classname, "monster_parasite"))? "a Parasite" :
614 						(!Q_stricmp (attacker->classname, "monster_tank"))? "a Ground Tank" :
615 						(!Q_stricmp (attacker->classname, "monster_tank_commander"))? "a Tank Commander" :
616 						(!Q_stricmp (attacker->classname, "monster_supertank"))? "a Super Ground Tank" :
617 						(!Q_stricmp (attacker->classname, "monster_boss2"))? "an Air Tank" :
618 						(!Q_stricmp (attacker->classname, "monster_boss3_stand"))? "a Strogg Makron" :
619 						(!Q_stricmp (attacker->classname, "monster_makron"))? "a Strogg Makron" :
620 						(!Q_stricmp (attacker->classname, "monster_jorg"))? "a Strogg Jorg" :
621 						"an enemy"), message2);
622 
623 				if (attacker->client)
624 				{
625 					if (ff)
626 						attacker->client->resp.score--;
627 					else
628 					{
629 						attacker->client->resp.score++;
630 						if (headShot)
631 							attacker->client->resp.headshots++;
632 
633 					}
634 				}
635 				return;
636 			}
637 		}
638 	}
639 	if (self)
640 		if (self->client)
641 			make_white(self->client->pers.netname);
642 	if (attacker)
643 		if (attacker->client)
644 			make_white(attacker->client->pers.netname);
645 
646 	gi.bprintf (PRINT_MEDIUM,"%s died.\n", make_white(self->client->pers.netname));
647 	if (deathmatch->value)
648 		self->client->resp.score--;
649 }
650 
651 
652 void Touch_Item (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf);
653 
TossClientWeapon(edict_t * self)654 void TossClientWeapon (edict_t *self)
655 {
656 	gitem_t		*item;
657 	edict_t		*drop;
658 	qboolean	quad;
659 	float		spread;
660 
661 	if (!deathmatch->value)
662 		return;
663 
664 	item = self->client->pers.weapon;
665 	if (! self->client->pers.inventory[self->client->ammo_index] )
666 		item = NULL;
667 	if (item && (strcmp (item->pickup_name, "Blaster") == 0))
668 		item = NULL;
669 
670 	if (!((int)(dmflags->value) & DF_QUAD_DROP))
671 		quad = false;
672 	else
673 		quad = (self->client->quad_framenum > (level.framenum + 10));
674 
675 	if (item && quad)
676 		spread = 22.5;
677 	else
678 		spread = 0.0;
679 
680 	if (item)
681 	{
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 (quad)
689 	{
690 		self->client->v_angle[YAW] += spread;
691 		drop = Drop_Item (self, FindItemByClassname ("item_quad"));
692 		self->client->v_angle[YAW] -= spread;
693 		drop->spawnflags |= DROPPED_PLAYER_ITEM;
694 
695 		drop->touch = Touch_Item;
696 		drop->nextthink = level.time + (self->client->quad_framenum - level.framenum) * FRAMETIME;
697 		drop->think = G_FreeEdict;
698 	}
699 }
700 
701 
702 /*
703 ==================
704 LookAtKiller
705 ==================
706 */
LookAtKiller(edict_t * self,edict_t * inflictor,edict_t * attacker)707 void LookAtKiller (edict_t *self, edict_t *inflictor, edict_t *attacker)
708 {
709 	vec3_t		dir;
710 
711 	if (attacker && attacker != world && attacker != self)
712 	{
713 		VectorSubtract (attacker->s.origin, self->s.origin, dir);
714 	}
715 	else if (inflictor && inflictor != world && inflictor != self)
716 	{
717 		VectorSubtract (inflictor->s.origin, self->s.origin, dir);
718 	}
719 	else
720 	{
721 		self->client->killer_yaw = self->s.angles[YAW];
722 		return;
723 	}
724 
725 	if (dir[0])
726 		self->client->killer_yaw = 180/M_PI*atan2(dir[1], dir[0]);
727 	else {
728 		self->client->killer_yaw = 0;
729 		if (dir[1] > 0)
730 			self->client->killer_yaw = 90;
731 		else if (dir[1] < 0)
732 			self->client->killer_yaw = -90;
733 	}
734 	if (self->client->killer_yaw < 0)
735 		self->client->killer_yaw += 360;
736 
737 
738 }
739 
740 /*
741 ==================
742 player_die
743 ==================
744 */
745 #define MOD_GRENADE			6
746 #define MOD_G_SPLASH		7
747 #define MOD_ROCKET			8
748 #define MOD_R_SPLASH		9
749 #define MOD_BFG_LASER		12
750 #define MOD_BFG_BLAST		13
751 #define MOD_HANDGRENADE		15
752 #define MOD_HG_SPLASH		16
753 #define MOD_LAVA			19
754 #define MOD_EXPLOSIVE		25
755 #define MOD_BARREL			26
756 #define MOD_BOMB			27
757 #define MOD_C4				35
758 #define MOD_GREN_HIT		36
759 #define MOD_FIRE			37
760 #define MOD_ROCKET_BFG		39
761 #define MOD_R_BFG_SPLASH	40
762 #define MOD_MAGIC			41
763 #define MOD_GRENADE2		47
764 #define MOD_G_SPLASH2		48
765 #define MOD_ROCKET2			49
766 #define MOD_FLAME			55
767 #define MOD_GAS_BOOM		56
768 #define MOD_DISCHARGE		58
769 
player_die(edict_t * self,edict_t * inflictor,edict_t * attacker,int damage,vec3_t point)770 void player_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
771 {
772 	int mod = meansOfDeath & ~MOD_FRIENDLY_FIRE;
773 	edict_t *ent, *newBody;
774 	int		n, j;
775 
776 	switch (mod)
777 	{
778 		case MOD_ROCKET:
779 		case MOD_BFG_LASER:
780 		case MOD_BFG_BLAST:
781 		case MOD_HANDGRENADE:
782 		case MOD_HELD_GRENADE:
783 		case MOD_BOMB:
784 		case MOD_C4:
785 		case MOD_GAS_BOOM:
786 			self->health = -500;
787 			break;
788 		case MOD_FLAME:
789 		case MOD_DISCHARGE:
790 		case MOD_LAVA:
791 			self->health -= damage * 2;
792 			break;
793 		case MOD_HG_SPLASH:
794 		case MOD_R_SPLASH:
795 		case MOD_G_SPLASH:
796 			self->health -= damage * 5;
797 			break;
798 		case MOD_R_BFG_SPLASH:
799 			self->health -= damage * 10;
800 			break;
801 	}
802 
803 	VectorClear (self->avelocity);
804 
805 	self->takedamage = DAMAGE_YES;
806 	self->movetype = MOVETYPE_TOSS;
807 
808 	self->s.modelindex2 = 0;	// remove linked weapon model
809 
810 	self->client->flashlight_on	= 0;	//turn off flashlight
811 	self->client->laser_on		= 0;	//turn off laser sight
812 	self->client->aquasuit		= 0;	//take off aqua suit
813 	self->client->goggles		= 0;	//take off goggles
814 
815 	self->timer	= 0;	//take off goggles
816 
817 	self->flaming = 0;
818 
819 	self->client->jets = 0;
820 
821 	//SET WEAP MODES
822 	self->client->gren_set		= 0;
823 	self->client->rock_set		= 0;
824 	self->client->chan_set		= 0;
825 	self->client->shot_set		= 0;
826 	self->client->mach_set		= 0;
827 	self->client->blst_set		= 0;
828 	self->client->hypr_set		= 0;
829 	self->client->bfg_set		= 0;
830 	self->client->rail_set		= 0;
831 	//SET WEAP MODES
832 
833 	self->client->ps.rdflags &= ~RDF_IRGOGGLES;
834 	self->client->climbing		= 0;
835 	self->flashbanged = 0;
836 	self->client->isOnTurret = 0;
837 	self->client->weaphold		= 0;	//flag for laser sight
838 
839 	self->s.angles[0] = 0;
840 	self->s.angles[2] = 0;
841 
842 	self->s.sound = 0;
843 	self->client->weapon_sound = 0;
844 
845 	self->maxs[2] = -8;
846 
847 	self->floater = 1;
848 
849 //	self->solid = SOLID_NOT;
850 	self->svflags |= SVF_DEADMONSTER;
851 
852 	if (self->client->kami==666)
853 	{
854 		self->s.effects	 &=	~EF_TELEPORTER;
855 		self->s.renderfx &=	~RF_SHELL_DOUBLE;
856 		self->client->kami=0;
857 		self->health = -555;
858 		T_RadiusDamage(self, self, 2500, self, 400, MOD_C4);
859 		if (sv_serversideonly->value)
860 			T_FlashRadius(self, self, 2500, self, 400);
861 
862 		target_earthquake_think (self);
863 		bigExplosion(self->s.origin, vec3_origin, 2);
864 		if (sv_serversideonly->value)
865 			ExplodeMark (self, self->s.origin, level.time + 5 + random()*2 );
866 	}
867 
868 	if (!self->deadflag)
869 	{
870 		self->client->respawn_time = level.time + 1.0;
871 		LookAtKiller (self, inflictor, attacker);
872 		self->client->ps.pmove.pm_type = PM_DEAD;
873 		ClientObituary (self, inflictor, attacker);
874 		TossClientWeapon (self);
875 		if (deathmatch->value)
876 			Cmd_Help_f (self);		// show scores
877 
878 		// clear inventory
879 		// this is kind of ugly, but it's how we want to handle keys in coop
880 		for (n = 0; n < game.num_items; n++)
881 		{
882 			if (coop->value && itemlist[n].flags & IT_KEY)
883 				self->client->resp.coop_respawn.inventory[n] = self->client->pers.inventory[n];
884 			self->client->pers.inventory[n] = 0;
885 		}
886 	}
887 
888 	//remove linked ents
889 	for (n= 0; n < self->linked_ents_num; n++)
890 	{
891 		if (self->linked_ents[n])
892 		{
893 			self->linked_ents[n]->movetype = MOVETYPE_TOSS;
894 			self->linked_ents[n]->linkedto=NULL;
895 			VectorClear(self->linked_ents[n]->velocity);
896 		}
897 	}
898 	self->linked_ents_num = 0;
899 
900 	// remove powerups
901 	self->client->quad_framenum = 0;
902 	self->client->invincible_framenum = 0;
903 	self->client->breather_framenum = 0;
904 	self->client->enviro_framenum = 0;
905 	self->flags &= ~FL_POWER_ARMOR;
906 
907 	if (self->health <= -500)
908 	{	// gib
909 		gi.sound (self, CHAN_BODY, gi.soundindex ("misc/udeath.wav"), 1, ATTN_NORM, 0);
910 		for (n= 0; n < 2; n++)
911 			ThrowGib (self, "models/objects/gibs/bone/tris.md2", damage, GIB_ORGANIC);
912 		for (n= 0; n < 4; n++)
913 			ThrowGib (self, "models/objects/gibs/sm_meat/tris.md2", damage, GIB_ORGANIC);
914 		ThrowGib (self, "models/objects/gibs/chest/tris.md2", damage, GIB_ORGANIC);
915 		//ThrowClientHead (self, damage);
916 		newBody = ThrowClientHeadNew (self);
917 
918 		self->takedamage = DAMAGE_NO;
919 	}
920 	else
921 	{	// normal death
922 		if (!self->deadflag)
923 		{
924 			static int i;
925 
926 			i = (i+1)%3;
927 			// start a death animation
928 			self->client->anim_priority = ANIM_DEATH;
929 			if (self->client->ps.pmove.pm_flags & PMF_DUCKED)
930 			{
931 				self->s.frame = FRAME_crdeath1;
932 				self->client->anim_end = FRAME_crdeath5;
933 			}
934 			else switch (i)
935 			{
936 			case 0:
937 				self->s.frame = FRAME_death101;
938 				self->client->anim_end = FRAME_death106;
939 				break;
940 			case 1:
941 				self->s.frame = FRAME_death201;
942 				self->client->anim_end = FRAME_death206;
943 				break;
944 			case 2:
945 				self->s.frame = FRAME_death301;
946 				self->client->anim_end = FRAME_death308;
947 				break;
948 			}
949 			gi.sound (self, CHAN_VOICE, gi.soundindex(va("*death%i.wav", (rand()%4)+1)), 1, ATTN_NORM, 0);
950 
951 
952 		}
953 		newBody=MakeNewBody (self);
954 	}
955 
956 	self->deadflag = DEAD_DEAD;
957 
958 	if (!deathmatch->value||self->viewcam_on ||true)
959 	{
960 		if (attacker!=self&&(attacker->client || (attacker->svflags & SVF_MONSTER))&&self->linked_flame!=attacker)
961 		{
962 			self->killer = attacker;
963 			self->solid = SOLID_NOT;
964 			self->movetype = MOVETYPE_NOCLIP;
965 		}
966 		else
967 		{
968 			self->killer = newBody;
969 			self->solid = SOLID_NOT;
970 			self->movetype = MOVETYPE_NOCLIP;
971 		}
972 		self->viewcam_on = 2;
973 	}
974 
975 	if(self->linked_flame)
976 	{
977 		self->linked_flame->flamed = newBody;
978 		self->linked_flame=NULL;
979 	}
980 
981 	for (j = 1; j <= game.maxentities; j++)
982 	{
983 		ent = &g_edicts[j];
984 
985 		if (ent->linkedto==self)
986 		{
987 			ent->linkedto = NULL;
988 			ent->movetype = MOVETYPE_TOSS;
989 		}
990 		if (newBody)
991 		{
992 			if (ent->killer==self)
993 				ent->killer=newBody;
994 			if (ent->enemy==self)
995 				ent->enemy=newBody;
996 		}
997 	}
998 
999 	self->client->viewcam_dist = 0;
1000 	CleanUpEnt (self);
1001 
1002 	gi.linkentity (self);
1003 
1004 }
1005 
1006 //=======================================================================
1007 
1008 /*
1009 ==============
1010 InitClientPersistant
1011 
1012 This is only called when the game first initializes in single player,
1013 but is called after each death and level change in deathmatch
1014 ==============
1015 */
InitClientPersistant(gclient_t * client)1016 void InitClientPersistant (gclient_t *client)
1017 {
1018 	gitem_t		*item;
1019 	int i;
1020 
1021 	memset (&client->pers, 0, sizeof(client->pers));
1022 
1023 		//*
1024 	//***My Section Start***
1025 	//*
1026 	for (i=0;i>50+(random()*20)+(random()*20);i++)
1027 	{
1028 		if (random()>.9)
1029 			i--;
1030 	}
1031 
1032 	if (sv_waterlevel->value)
1033 	{
1034 		item = FindItem("Flashlight");
1035 		client->pers.inventory[ITEM_INDEX(item)] = 1;
1036 		item = FindItem("Laser Sight");
1037 		client->pers.inventory[ITEM_INDEX(item)] = 1;
1038 	}
1039 	else if (deathmatch->value)
1040 	{
1041 
1042 		item = FindItem("Flashlight");
1043 		client->pers.inventory[ITEM_INDEX(item)] = 1;
1044 
1045 		if (sv_grapple->value)
1046 		{
1047 			item = FindItem("Grapple");
1048 				client->pers.inventory[ITEM_INDEX(item)] = 1;
1049 		}
1050 
1051 		if (random()<0.2 && (int)sv_banned_weapons->value!=-1)
1052 		{
1053 			item = FindItem("Laser Sight");
1054 			client->pers.inventory[ITEM_INDEX(item)] = 1;
1055 		}
1056 		else if (crandom()<0.2)
1057 		{
1058 			item = FindItem("Diving Mask");
1059 			client->pers.inventory[ITEM_INDEX(item)] = 1;
1060 		}
1061 		else if (crandom()<0.2)
1062 		{
1063 			item = FindItem("Jet Propulsion Unit");
1064 			client->pers.inventory[ITEM_INDEX(item)] = 1;
1065 		}
1066 		else if ((int)sv_banned_weapons->value!=-1)
1067 		{
1068 			item = FindItem("Regen-Stealth Suit");
1069 			client->pers.inventory[ITEM_INDEX(item)] = 1;
1070 		}
1071 		else
1072 		{
1073 			item = FindItem("Jet Propulsion Unit");
1074 			client->pers.inventory[ITEM_INDEX(item)] = 1;
1075 		}
1076 
1077 	}
1078 	else if (coop->value)
1079 	{
1080 		if (sv_grapple->value)
1081 		{
1082 			item = FindItem("Grapple");
1083 				client->pers.inventory[ITEM_INDEX(item)] = 1;
1084 		}
1085 	}
1086 	//***My Section End***/
1087 
1088 	if ((int)sv_banned_weapons->value!=-1)
1089 	{
1090 		int banned = sv_banned_weapons->value;
1091 
1092 		if (!(banned&NO_BLASTER))
1093 		{
1094 			item = FindItem("Bullets");
1095 			client->pers.inventory[ITEM_INDEX(item)] = 100;
1096 			item = FindItem("Blaster");
1097 			client->pers.selected_item = ITEM_INDEX(item);
1098 			client->pers.inventory[client->pers.selected_item] = 1;
1099 		}
1100 		else if (!(banned&NO_SHOTGUN))
1101 		{
1102 			item = FindItem("Shells");
1103 			client->pers.inventory[ITEM_INDEX(item)] = 100;
1104 			item = FindItem("Shotgun");
1105 			client->pers.selected_item = ITEM_INDEX(item);
1106 			client->pers.inventory[client->pers.selected_item] = 1;
1107 		}
1108 		else if (!(banned&NO_SSHOTGUN))
1109 		{
1110 			item = FindItem("Shells");
1111 			client->pers.inventory[ITEM_INDEX(item)] = 100;
1112 			item = FindItem("Super Shotgun");
1113 			client->pers.selected_item = ITEM_INDEX(item);
1114 			client->pers.inventory[client->pers.selected_item] = 1;
1115 		}
1116 		else if (!(banned&NO_MACHINEGUN))
1117 		{
1118 			item = FindItem("Bullets");
1119 			client->pers.inventory[ITEM_INDEX(item)] = 100;
1120 			item = FindItem("Mashinegun");
1121 			client->pers.selected_item = ITEM_INDEX(item);
1122 			client->pers.inventory[client->pers.selected_item] = 1;
1123 		}
1124 		else if (!(banned&NO_CHAINGUN))
1125 		{
1126 			item = FindItem("Shells");
1127 			client->pers.inventory[ITEM_INDEX(item)] = 100;
1128 			item = FindItem("Chaingun");
1129 			client->pers.selected_item = ITEM_INDEX(item);
1130 			client->pers.inventory[client->pers.selected_item] = 1;
1131 		}
1132 		else if (!(banned&NO_GRENADE_LAUNCHER))
1133 		{
1134 			item = FindItem("grenades");
1135 			client->pers.inventory[ITEM_INDEX(item)] = 20;
1136 			item = FindItem("Grenade Launcher");
1137 			client->pers.selected_item = ITEM_INDEX(item);
1138 			client->pers.inventory[client->pers.selected_item] = 1;
1139 		}
1140 		else if (!(banned&NO_ROCKET_LAUNCHER))
1141 		{
1142 			item = FindItem("rockets");
1143 			client->pers.inventory[ITEM_INDEX(item)] = 15;
1144 			item = FindItem("Rocket Launcher");
1145 			client->pers.selected_item = ITEM_INDEX(item);
1146 			client->pers.inventory[client->pers.selected_item] = 1;
1147 		}
1148 		else if (!(banned&NO_HYPERBLASTER))
1149 		{
1150 			item = FindItem("Cells");
1151 			client->pers.inventory[ITEM_INDEX(item)] = 100;
1152 			item = FindItem("HyperBlaster");
1153 			client->pers.selected_item = ITEM_INDEX(item);
1154 			client->pers.inventory[client->pers.selected_item] = 1;
1155 		}
1156 		else if (!(banned&NO_RAILGUN) && !(banned&NO_TAZER))
1157 		{
1158 			if (!(banned&NO_RAILGUN))
1159 			{
1160 				item = FindItem("Slugs");
1161 				client->pers.inventory[ITEM_INDEX(item)] = 10;
1162 			}
1163 			if (!(banned&NO_TAZER))
1164 			{
1165 				item = FindItem("Cells");
1166 				client->pers.inventory[ITEM_INDEX(item)] = 75;
1167 			}
1168 			item = FindItem("Railgun");
1169 			client->pers.selected_item = ITEM_INDEX(item);
1170 			client->pers.inventory[client->pers.selected_item] = 1;
1171 		}
1172 		else if (!(banned&NO_ROCKETBFG) && !(banned&NO_BFGLASER))
1173 		{
1174 			if (!(banned&NO_ROCKETBFG))
1175 			{
1176 				item = FindItem("Rockets");
1177 				client->pers.inventory[ITEM_INDEX(item)] = 100;
1178 			}
1179 			if (!(banned&NO_BFGLASER))
1180 			{
1181 				item = FindItem("Cells");
1182 				client->pers.inventory[ITEM_INDEX(item)] = 100;
1183 			}
1184 			item = FindItem("BFG10K");
1185 			client->pers.selected_item = ITEM_INDEX(item);
1186 			client->pers.inventory[client->pers.selected_item] = 1;
1187 		}
1188 		else if (!(banned&NO_GRENADES))
1189 		{
1190 			item = FindItem("Grenades");
1191 			client->pers.selected_item = ITEM_INDEX(item);
1192 			client->pers.inventory[client->pers.selected_item] = 10;
1193 		}
1194 		base_weapon = item->pickup_name;
1195 	}
1196 
1197 	client->pers.weapon = item;
1198 
1199 	client->pers.health			= 100;
1200 	client->pers.max_health		= 100;
1201 
1202 	client->pers.max_bullets	= 200;
1203 	client->pers.max_shells		= 200;
1204 	client->pers.max_rockets	= 100;
1205 	client->pers.max_grenades	= 25;
1206 	client->pers.max_cells		= 100;
1207 	client->pers.max_slugs		= 10;
1208 
1209 	client->pers.connected = true;
1210 }
1211 
1212 
InitClientResp(gclient_t * client)1213 void InitClientResp (gclient_t *client)
1214 {
1215 	memset (&client->resp, 0, sizeof(client->resp));
1216 	client->resp.enterframe = level.framenum;
1217 	client->resp.coop_respawn = client->pers;
1218 }
1219 
1220 /*
1221 ==================
1222 SaveClientData
1223 
1224 Some information that should be persistant, like health,
1225 is still stored in the edict structure, so it needs to
1226 be mirrored out to the client structure before all the
1227 edicts are wiped.
1228 ==================
1229 */
SaveClientData(void)1230 void SaveClientData (void)
1231 {
1232 	int		i;
1233 	edict_t	*ent;
1234 
1235 	for (i=0 ; i<game.maxclients ; i++)
1236 	{
1237 		ent = &g_edicts[1+i];
1238 		if (!ent->inuse)
1239 			continue;
1240 
1241         game.clients[i].pers.chasetoggle = ent->client->chasetoggle;
1242 
1243 		game.clients[i].pers.health = ent->health;
1244 		game.clients[i].pers.max_health = ent->max_health;
1245 		game.clients[i].pers.savedFlags = (ent->flags & (FL_GODMODE|FL_NOTARGET|FL_POWER_ARMOR));
1246 
1247 		game.clients[i].pers.grappleType	= ent->grappleType;
1248 		game.clients[i].pers.bfg_laser_type	= ent->bfg_laser_type;
1249 		game.clients[i].pers.Player_ID		= ent->Player_ID;
1250 		game.clients[i].pers.TeamName		= ent->TeamName;
1251 		game.clients[i].pers.viewcam_on		= ent->viewcam_on;
1252 
1253 		if (coop->value)
1254 		{
1255 			game.clients[i].pers.score = ent->client->resp.score;
1256 			game.clients[i].pers.headshots = ent->client->resp.headshots;
1257 		}
1258 	}
1259 }
1260 
FetchClientEntData(edict_t * ent)1261 void FetchClientEntData (edict_t *ent)
1262 {
1263 	ent->health = ent->client->pers.health;
1264 	ent->max_health = ent->client->pers.max_health;
1265 	ent->flags |= ent->client->pers.savedFlags;
1266 
1267 	if (!deathmatch->value&&!coop->value)
1268 	{
1269 		ent->grappleType	= ent->client->pers.grappleType;
1270 		ent->bfg_laser_type = ent->client->pers.bfg_laser_type;
1271 		ent->Player_ID		= ent->client->pers.Player_ID;
1272 		ent->TeamName		= ent->client->pers.TeamName;
1273 		ent->viewcam_on		= ent->client->pers.viewcam_on;
1274 	}
1275 
1276 	if (coop->value)
1277 	{
1278 		ent->client->resp.score = ent->client->pers.score;
1279 		ent->client->resp.headshots = ent->client->pers.headshots;
1280 	}
1281 }
1282 
1283 
1284 
1285 /*
1286 =======================================================================
1287 
1288   SelectSpawnPoint
1289 
1290 =======================================================================
1291 */
1292 
1293 /*
1294 ================
1295 PlayersRangeFromSpot
1296 
1297 Returns the distance to the nearest player from the given spot
1298 ================
1299 */
PlayersRangeFromSpot(edict_t * spot)1300 float	PlayersRangeFromSpot (edict_t *spot)
1301 {
1302 	edict_t	*player;
1303 	float	bestplayerdistance;
1304 	vec3_t	v;
1305 	int		n;
1306 	float	playerdistance;
1307 
1308 
1309 	bestplayerdistance = 9999999;
1310 
1311 	for (n = 1; n <= maxclients->value; n++)
1312 	{
1313 		player = &g_edicts[n];
1314 
1315 		if (!player->inuse)
1316 			continue;
1317 
1318 		if (player->health <= 0)
1319 			continue;
1320 
1321 		VectorSubtract (spot->s.origin, player->s.origin, v);
1322 		playerdistance = VectorLength (v);
1323 
1324 		if (playerdistance < bestplayerdistance)
1325 			bestplayerdistance = playerdistance;
1326 	}
1327 
1328 	return bestplayerdistance;
1329 }
1330 
1331 /*
1332 ================
1333 SelectRandomDeathmatchSpawnPoint
1334 
1335 go to a random point, but NOT the two points closest
1336 to other players
1337 ================
1338 */
SelectRandomDeathmatchSpawnPoint(void)1339 edict_t *SelectRandomDeathmatchSpawnPoint (void)
1340 {
1341 	edict_t	*spot, *spot1, *spot2;
1342 	int		count = 0;
1343 	int		selection;
1344 	float	range, range1, range2;
1345 
1346 	spot = NULL;
1347 	range1 = range2 = 99999;
1348 	spot1 = spot2 = NULL;
1349 
1350 	while ((spot = G_Find (spot, FOFS(classname), "info_player_deathmatch")) != NULL)
1351 	{
1352 		count++;
1353 		range = PlayersRangeFromSpot(spot);
1354 		if (range < range1)
1355 		{
1356 			range1 = range;
1357 			spot1 = spot;
1358 		}
1359 		else if (range < range2)
1360 		{
1361 			range2 = range;
1362 			spot2 = spot;
1363 		}
1364 	}
1365 
1366 	if (!count)
1367 		return NULL;
1368 
1369 	if (count <= 2)
1370 	{
1371 		spot1 = spot2 = NULL;
1372 	}
1373 	else
1374 		count -= 2;
1375 
1376 	selection = rand() % count;
1377 
1378 	spot = NULL;
1379 	do
1380 	{
1381 		spot = G_Find (spot, FOFS(classname), "info_player_deathmatch");
1382 		if (spot == spot1 || spot == spot2)
1383 			selection++;
1384 	} while(selection--);
1385 
1386 	return spot;
1387 }
1388 
1389 /*
1390 ================
1391 SelectFarthestDeathmatchSpawnPoint
1392 
1393 ================
1394 */
SelectFarthestDeathmatchSpawnPoint(void)1395 edict_t *SelectFarthestDeathmatchSpawnPoint (void)
1396 {
1397 	edict_t	*bestspot;
1398 	float	bestdistance, bestplayerdistance;
1399 	edict_t	*spot;
1400 
1401 
1402 	spot = NULL;
1403 	bestspot = NULL;
1404 	bestdistance = 0;
1405 	while ((spot = G_Find (spot, FOFS(classname), "info_player_deathmatch")) != NULL)
1406 	{
1407 		bestplayerdistance = PlayersRangeFromSpot (spot);
1408 
1409 		if (bestplayerdistance > bestdistance)
1410 		{
1411 			bestspot = spot;
1412 			bestdistance = bestplayerdistance;
1413 		}
1414 	}
1415 
1416 	if (bestspot)
1417 	{
1418 		return bestspot;
1419 	}
1420 
1421 	// if there is a player just spawned on each and every start spot
1422 	// we have no choice to turn one into a telefrag meltdown
1423 	spot = G_Find (NULL, FOFS(classname), "info_player_deathmatch");
1424 
1425 	return spot;
1426 }
1427 
SelectDeathmatchSpawnPoint(void)1428 edict_t *SelectDeathmatchSpawnPoint (void)
1429 {
1430 	if ( (int)(dmflags->value) & DF_SPAWN_FARTHEST)
1431 		return SelectFarthestDeathmatchSpawnPoint ();
1432 	else
1433 		return SelectRandomDeathmatchSpawnPoint ();
1434 }
1435 
1436 
SelectCoopSpawnPoint(edict_t * ent)1437 edict_t *SelectCoopSpawnPoint (edict_t *ent)
1438 {
1439 	int		index;
1440 	edict_t	*spot = NULL;
1441 	char	*target;
1442 
1443 	index = ent->client - game.clients;
1444 
1445 	// player 0 starts in normal player spawn point
1446 	if (!index)
1447 		return NULL;
1448 
1449 	spot = NULL;
1450 
1451 	// assume there are four coop spots at each spawnpoint
1452 	while (1)
1453 	{
1454 		spot = G_Find (spot, FOFS(classname), "info_player_coop");
1455 		if (!spot)
1456 			return NULL;	// we didn't have enough...
1457 
1458 		target = spot->targetname;
1459 		if (!target)
1460 			target = "";
1461 		if ( Q_stricmp(game.spawnpoint, target) == 0 )
1462 		{	// this is a coop spawn point for one of the clients here
1463 			index--;
1464 			if (!index)
1465 				return spot;		// this is it
1466 		}
1467 	}
1468 
1469 
1470 	return spot;
1471 }
1472 
1473 
1474 /*
1475 ===========
1476 SelectSpawnPoint
1477 
1478 Chooses a player start, deathmatch start, coop start, etc
1479 ============
1480 */
SelectSpawnPoint(edict_t * ent,vec3_t origin,vec3_t angles)1481 void	SelectSpawnPoint (edict_t *ent, vec3_t origin, vec3_t angles)
1482 {
1483 	edict_t	*spot = NULL;
1484 
1485 	if (deathmatch->value)
1486 		spot = SelectDeathmatchSpawnPoint ();
1487 	else if (coop->value)
1488 		spot = SelectCoopSpawnPoint (ent);
1489 
1490 	// find a single player start spot
1491 	if (!spot)
1492 	{
1493 		while ((spot = G_Find (spot, FOFS(classname), "info_player_start")) != NULL)
1494 		{
1495 			if (!game.spawnpoint[0] && !spot->targetname)
1496 				break;
1497 
1498 			if (!game.spawnpoint[0] || !spot->targetname)
1499 				continue;
1500 
1501 			if (Q_stricmp(game.spawnpoint, spot->targetname) == 0)
1502 				break;
1503 		}
1504 
1505 		if (!spot)
1506 		{
1507 			if (!game.spawnpoint[0])
1508 			{	// there wasn't a spawnpoint without a target, so use any
1509 				spot = G_Find (spot, FOFS(classname), "info_player_start");
1510 			}
1511 			if (!spot)
1512 				gi.error ("Couldn't find spawn point %s\n", game.spawnpoint);
1513 		}
1514 	}
1515 
1516 	VectorCopy (spot->s.origin, origin);
1517 	origin[2] += 9;
1518 	VectorCopy (spot->s.angles, angles);
1519 }
1520 
1521 //======================================================================
1522 
1523 
InitBodyQue(void)1524 void InitBodyQue (void)
1525 {
1526 	int		i;
1527 	edict_t	*ent;
1528 
1529 	level.body_que = 0;
1530 	for (i=0; i<BODY_QUEUE_SIZE ; i++)
1531 	{
1532 		ent = G_Spawn();
1533 		ent->classname = "bodyque";
1534 	}
1535 }
1536 
body_die(edict_t * self,edict_t * inflictor,edict_t * attacker,int damage,vec3_t point)1537 void body_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
1538 {
1539 	int	n;
1540 	int dmg = 0;
1541 	if (self->health < -500)
1542 	{
1543 		gi.sound (self, CHAN_BODY, gi.soundindex ("misc/udeath.wav"), 1, ATTN_NORM, 0);
1544 
1545 		for (n= 0; n < 2; n++)
1546 			ThrowGib (self, "models/objects/gibs/bone/tris.md2", dmg, GIB_ORGANIC);
1547 		for (n= 0; n < 4; n++)
1548 			ThrowGib (self, "models/objects/gibs/sm_meat/tris.md2", dmg, GIB_ORGANIC);
1549 		ThrowGib (self, "models/objects/gibs/chest/tris.md2", dmg, GIB_ORGANIC);
1550 		self->takedamage = DAMAGE_NO;
1551 		ThrowClientHeadNew (self);
1552 		G_FreeEdict(self);
1553 		return;
1554 	}
1555 
1556 }
1557 
body_touch(edict_t * ent,edict_t * other,cplane_t * plane,csurface_t * surf)1558 static void body_touch (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf)
1559 {
1560 	if (!other->takedamage)
1561 	{
1562 		if (!(surf && (surf->flags & SURF_SKY)))
1563 				gi.sound (ent, CHAN_VOICE, gi.soundindex ("world/land.wav"), 0.5, ATTN_NORM, 0);
1564 	}
1565 }
1566 
body_think(edict_t * ent)1567 void body_think (edict_t *ent)
1568 {
1569 	if (ent->s.frame==FRAME_crdeath5 || ent->s.frame==FRAME_death106 ||
1570 		ent->s.frame==FRAME_death206 || ent->s.frame==FRAME_death308)
1571 	{
1572 		ent->think=FadeDieSink;
1573 		ent->nextthink=level.time+FADETIME+random()*5;
1574 		return;
1575 	}
1576 
1577 	if (ent->s.frame>=135 && ent->s.frame<=172)
1578 		ent->s.frame=FRAME_crdeath1;
1579 	else if (ent->s.frame<135)
1580 	{
1581 		if ((int)(crandom()*3)==1)
1582 			ent->s.frame=FRAME_death101;
1583 		else if ((int)(crandom()*3)==1)
1584 			ent->s.frame=FRAME_death201;
1585 		else
1586 			ent->s.frame=FRAME_death301;
1587 	}
1588 	else if ((ent->s.frame>=FRAME_crdeath1 && ent->s.frame<=FRAME_crdeath4)||
1589 		(ent->s.frame>=FRAME_death101 && ent->s.frame<=FRAME_death105)||
1590 		(ent->s.frame>=FRAME_death201 && ent->s.frame<=FRAME_death205)||
1591 		(ent->s.frame>=FRAME_death301 && ent->s.frame<=FRAME_death307))
1592 
1593 	{
1594 		ent->s.frame++;
1595 	}
1596 
1597 	ent->nextthink=level.time+FRAMETIME;
1598 }
1599 
MakeNewBody(edict_t * ent)1600 edict_t * MakeNewBody (edict_t *ent)
1601 {
1602 	edict_t		*body;
1603 
1604 	body=G_Spawn();
1605 
1606 	body->s = ent->s;
1607 	body->s.number = body - g_edicts;
1608 	VectorCopy (ent->mins, body->mins);
1609 	VectorCopy (ent->maxs, body->maxs);
1610 	VectorCopy (ent->absmin, body->absmin);
1611 	VectorCopy (ent->absmax, body->absmax);
1612 	VectorCopy (ent->size, body->size);
1613 	VectorCopy (ent->velocity, body->velocity);
1614 	body->solid = ent->solid;
1615 	body->clipmask = ent->clipmask;
1616 	body->owner = ent->owner;
1617 	body->movetype = ent->movetype;
1618 
1619 	body->svflags = SVF_DEADMONSTER;
1620 
1621 	body->die = body_die;
1622 	body->takedamage = DAMAGE_YES;
1623 	body->movetype = MOVETYPE_TOSS;
1624 
1625 	body->touch = body_touch;
1626 
1627 	body->think=body_think;
1628 	body->nextthink=level.time+FRAMETIME;
1629 	body->floater=1;
1630 
1631 	body->s.renderfx |= RF_IR_VISIBLE;
1632 
1633 	if (ent->client)
1634 		body->PlayerDeadName = ent->client->pers.netname;
1635 
1636 	gi.linkentity (body);
1637 
1638 	return body;
1639 }
1640 
CopyToBodyQue(edict_t * ent)1641 void CopyToBodyQue (edict_t *ent)
1642 {
1643 	edict_t		*body;
1644 
1645 	// grab a body que and cycle to the next one
1646 	body = &g_edicts[(int)maxclients->value + level.body_que + 1];
1647 	level.body_que = (level.body_que + 1) % BODY_QUEUE_SIZE;
1648 
1649 	// FIXME: send an effect on the removed body
1650 
1651 	gi.unlinkentity (ent);
1652 
1653 	gi.unlinkentity (body);
1654 	body->s = ent->s;
1655 	body->s.number = body - g_edicts;
1656 
1657 	body->svflags = ent->svflags;
1658 	VectorCopy (ent->mins, body->mins);
1659 	VectorCopy (ent->maxs, body->maxs);
1660 	VectorCopy (ent->absmin, body->absmin);
1661 	VectorCopy (ent->absmax, body->absmax);
1662 	VectorCopy (ent->size, body->size);
1663 	body->solid = ent->solid;
1664 	body->clipmask = ent->clipmask;
1665 	body->owner = ent->owner;
1666 	body->movetype = ent->movetype;
1667 
1668 	body->die = body_die;
1669 	body->takedamage = DAMAGE_YES;
1670 
1671 	body->think=FadeDieSink;
1672 	body->nextthink=level.time+FADETIME+random()*5;
1673 	body->floater=1;
1674 
1675 	body->s.renderfx |= RF_IR_VISIBLE;
1676 
1677 	gi.linkentity (body);
1678 }
1679 
1680 
respawn(edict_t * self)1681 void respawn (edict_t *self)
1682 {
1683 	if (deathmatch->value || coop->value)
1684 	{
1685 		// spectator's don't leave bodies
1686 //		if (self->movetype != MOVETYPE_NOCLIP)
1687 //			CopyToBodyQue (self);
1688 		self->svflags &= ~SVF_NOCLIENT;
1689 		PutClientInServer (self);
1690 
1691 		// add a teleportation effect
1692 		self->s.event = EV_PLAYER_TELEPORT;
1693 
1694 		// hold in place briefly
1695 		self->client->ps.pmove.pm_flags = PMF_TIME_TELEPORT;
1696 		self->client->ps.pmove.pm_time = 1.1;
1697 
1698 		self->client->respawn_time = level.time;
1699 
1700 		return;
1701 	}
1702 
1703 	// restart the entire server
1704 	gi.AddCommandString ("menu_loadgame\n");
1705 }
1706 
1707 /*
1708  * only called when pers.spectator changes
1709  * note that resp.spectator should be the opposite of pers.spectator here
1710  */
spectator_respawn(edict_t * ent)1711 void spectator_respawn (edict_t *ent)
1712 {
1713 	int i, numspec;
1714 
1715 	// if the user wants to become a spectator, make sure he doesn't
1716 	// exceed max_spectators
1717 
1718 	if (ent->client->pers.spectator) {
1719 		char *value = Info_ValueForKey (ent->client->pers.userinfo, "spectator");
1720 		if (*spectator_password->string &&
1721 			strcmp(spectator_password->string, "none") &&
1722 			strcmp(spectator_password->string, value)) {
1723 			gi.cprintf(ent, PRINT_HIGH, "Spectator password incorrect.\n");
1724 			ent->client->pers.spectator = false;
1725 			gi.WriteByte (svc_stufftext);
1726 			gi.WriteString ("spectator 0\n");
1727 			gi.unicast(ent, true);
1728 			return;
1729 		}
1730 
1731 		// count spectators
1732 		for (i = 1, numspec = 0; i <= maxclients->value; i++)
1733 			if (g_edicts[i].inuse && g_edicts[i].client->pers.spectator)
1734 				numspec++;
1735 
1736 		if (numspec >= maxspectators->value) {
1737 			gi.cprintf(ent, PRINT_HIGH, "Server spectator limit is full.");
1738 			ent->client->pers.spectator = false;
1739 			// reset his spectator var
1740 			gi.WriteByte (svc_stufftext);
1741 			gi.WriteString ("spectator 0\n");
1742 			gi.unicast(ent, true);
1743 			return;
1744 		}
1745 	} else {
1746 		// he was a spectator and wants to join the game
1747 		// he must have the right password
1748 		char *value = Info_ValueForKey (ent->client->pers.userinfo, "password");
1749 		if (*password->string && strcmp(password->string, "none") &&
1750 			strcmp(password->string, value)) {
1751 			gi.cprintf(ent, PRINT_HIGH, "Password incorrect.\n");
1752 			ent->client->pers.spectator = true;
1753 			gi.WriteByte (svc_stufftext);
1754 			gi.WriteString ("spectator 1\n");
1755 			gi.unicast(ent, true);
1756 			return;
1757 		}
1758 	}
1759 
1760 	// clear client on respawn
1761 	ent->client->resp.score = ent->client->pers.score = 0;
1762 	ent->client->resp.headshots = ent->client->pers.headshots = 0;
1763 
1764 	ent->svflags &= ~SVF_NOCLIENT;
1765 	PutClientInServer (ent);
1766 
1767 	// add a teleportation effect
1768 	if (!ent->client->pers.spectator)  {
1769 		// send effect
1770 		gi.WriteByte (svc_muzzleflash);
1771 		gi.WriteShort (ent-g_edicts);
1772 		gi.WriteByte (MZ_LOGIN);
1773 		gi.multicast (ent->s.origin, MULTICAST_PVS);
1774 
1775 		// hold in place briefly
1776 		ent->client->ps.pmove.pm_flags = PMF_TIME_TELEPORT;
1777 		ent->client->ps.pmove.pm_time = 14;
1778 	}
1779 
1780 	ent->client->respawn_time = level.time;
1781 
1782 	if (ent->client->pers.spectator)
1783 		gi.bprintf (PRINT_HIGH, "%s has moved to the sidelines\n", ent->client->pers.netname);
1784 	else
1785 		gi.bprintf (PRINT_HIGH, "%s joined the game\n", ent->client->pers.netname);
1786 }
1787 
1788 //==============================================================
1789 
1790 
1791 /*
1792 ===========
1793 PutClientInServer
1794 
1795 Called when a player connects to a server or respawns in
1796 a deathmatch.
1797 ============
1798 */
PutClientInServer(edict_t * ent)1799 void PutClientInServer (edict_t *ent)
1800 {
1801 	vec3_t	mins = {-16, -16, -24};
1802 	vec3_t	maxs = {16, 16, 32};
1803 	int		index;
1804 	vec3_t	spawn_origin, spawn_angles;
1805 	gclient_t	*client;
1806 	int		i;
1807 	client_persistant_t	saved;
1808 	client_respawn_t	resp;
1809 	gitem_t		*item;
1810 
1811     int		chasetoggle;
1812     char	userinfo[MAX_INFO_STRING];
1813 
1814 	// find a spawn point
1815 	// do it before setting health back up, so farthest
1816 	// ranging doesn't count this client
1817 	SelectSpawnPoint (ent, spawn_origin, spawn_angles);
1818 
1819 	index = ent-g_edicts-1;
1820 	client = ent->client;
1821 
1822     chasetoggle = client->pers.chasetoggle;
1823 
1824 	// deathmatch wipes most client data every spawn
1825 	if (deathmatch->value)
1826 	{
1827 		char		userinfo[MAX_INFO_STRING];
1828 
1829 		resp = client->resp;
1830 		memcpy (userinfo, client->pers.userinfo, sizeof(userinfo));
1831 		InitClientPersistant (client);
1832 		ClientUserinfoChanged (ent, userinfo);
1833 	}
1834 	else if (coop->value)
1835 	{
1836 //		int			n;
1837 		char		userinfo[MAX_INFO_STRING];
1838 
1839 		resp = client->resp;
1840 		memcpy (userinfo, client->pers.userinfo, sizeof(userinfo));
1841 		// this is kind of ugly, but it's how we want to handle keys in coop
1842 //		for (n = 0; n < game.num_items; n++)
1843 //		{
1844 //			if (itemlist[n].flags & IT_KEY)
1845 //				resp.coop_respawn.inventory[n] = client->pers.inventory[n];
1846 //		}
1847 		resp.coop_respawn.game_helpchanged = client->pers.game_helpchanged;
1848 		resp.coop_respawn.helpchanged = client->pers.helpchanged;
1849 		client->pers = resp.coop_respawn;
1850 		ClientUserinfoChanged (ent, userinfo);
1851 		if (resp.score > client->pers.score)
1852 			client->pers.score = resp.score;
1853 		if (resp.headshots > client->pers.headshots)
1854 			client->pers.headshots = resp.headshots;
1855 	}
1856 	else
1857 	{
1858 		memset (&resp, 0, sizeof(resp));
1859 
1860 		memcpy (userinfo, client->pers.userinfo, sizeof(userinfo));
1861 		ClientUserinfoChanged (ent, userinfo);
1862 	}
1863 
1864 	// clear everything but the persistant data
1865 	saved = client->pers;
1866 	memset (client, 0, sizeof(*client));
1867 	client->pers = saved;
1868 	if (client->pers.health <= 0)
1869 		InitClientPersistant(client);
1870 	client->resp = resp;
1871 
1872     client->pers.chasetoggle = chasetoggle;
1873 
1874 	if (coop->value)
1875 	{
1876 		client->pers.health = 100;
1877 		ent->health = 100;
1878 	}
1879 
1880 	// copy some data from the client to the entity
1881 	FetchClientEntData (ent);
1882 
1883 	ent->linked_flame=NULL;
1884 
1885 	// clear entity values
1886 	ent->groundentity = NULL;
1887 	ent->client = &game.clients[index];
1888 	ent->takedamage = DAMAGE_AIM;
1889 	ent->movetype = MOVETYPE_WALK;
1890 	ent->viewheight = 22;
1891 	ent->inuse = true;
1892 	ent->classname = "player";
1893 	ent->mass = 200;
1894 	ent->solid = SOLID_BBOX;
1895 	ent->deadflag = DEAD_NO;
1896 	ent->air_finished = level.time + 12;
1897 	ent->clipmask = MASK_PLAYERSOLID;
1898 	ent->model = "players/male/tris.md2";
1899 	ent->pain = player_pain;
1900 	ent->die = player_die;
1901 	ent->waterlevel = 0;
1902 	ent->watertype = 0;
1903 	ent->flags &= ~FL_NO_KNOCKBACK;
1904 	ent->svflags &= ~SVF_DEADMONSTER;
1905 
1906     ent->svflags &= ~SVF_NOCLIENT;
1907     //Turn off prediction
1908     ent->client->ps.pmove.pm_flags &= ~PMF_NO_PREDICTION;
1909 
1910 	ent->killer = NULL;
1911 	ent->flashbanged = 0;
1912 	ent->floater = 1;
1913 
1914 	VectorCopy (mins, ent->mins);
1915 	VectorCopy (maxs, ent->maxs);
1916 	VectorClear (ent->velocity);
1917 
1918 	// clear playerstate values
1919 	memset (&ent->client->ps, 0, sizeof(client->ps));
1920 
1921 	client->ps.pmove.origin[0] = spawn_origin[0]*8;
1922 	client->ps.pmove.origin[1] = spawn_origin[1]*8;
1923 	client->ps.pmove.origin[2] = spawn_origin[2]*8;
1924 
1925 	if (deathmatch->value && ((int)dmflags->value & DF_FIXED_FOV))
1926 	{
1927 		client->ps.fov = 90;
1928 	}
1929 	else
1930 	{
1931 		client->ps.fov = atoi(Info_ValueForKey(client->pers.userinfo, "fov"));
1932 		if (client->ps.fov < 1)
1933 			client->ps.fov = 90;
1934 		else if (client->ps.fov > 160)
1935 			client->ps.fov = 160;
1936 	}
1937 
1938 	client->ps.gunindex = gi.modelindex(client->pers.weapon->view_model);
1939 
1940 	// clear entity state values
1941 	ent->s.effects = 0;
1942 	ent->s.modelindex = 255;		// will use the skin specified model
1943 	ent->s.modelindex2 = 255;		// custom gun model
1944 	// sknum is player num and weapon number
1945 	// weapon number will be added in changeweapon
1946 	ent->s.skinnum = ent - g_edicts - 1;
1947 
1948 	ent->s.frame = 0;
1949 	VectorCopy (spawn_origin, ent->s.origin);
1950 	ent->s.origin[2] += 1;	// make sure off ground
1951 	VectorCopy (ent->s.origin, ent->s.old_origin);
1952 
1953 	// set the delta angle
1954 	for (i=0 ; i<3 ; i++)
1955 	{
1956 		client->ps.pmove.delta_angles[i] = ANGLE2SHORT(spawn_angles[i] - client->resp.cmd_angles[i]);
1957 	}
1958 
1959 	ent->s.angles[PITCH] = 0;
1960 	ent->s.angles[YAW] = spawn_angles[YAW];
1961 	ent->s.angles[ROLL] = 0;
1962 	VectorCopy (ent->s.angles, client->ps.viewangles);
1963 	VectorCopy (ent->s.angles, client->v_angle);
1964 
1965 	// spawn a spectator
1966 	if (client->pers.spectator) {
1967 		client->chase_target = NULL;
1968 
1969 		client->resp.spectator = true;
1970 
1971 		ent->movetype = MOVETYPE_NOCLIP;
1972 		ent->solid = SOLID_NOT;
1973 		ent->svflags |= SVF_NOCLIENT;
1974 		ent->client->ps.gunindex = 0;
1975 		gi.linkentity (ent);
1976 		return;
1977 	} else
1978 		client->resp.spectator = false;
1979 
1980 	if (!KillBox (ent))
1981 	{	// could't spawn in?
1982 	}
1983 
1984 	gi.linkentity (ent);
1985 
1986 	ent->killer = NULL;
1987 
1988     ent->client->chasetoggle = 0;
1989     //If chasetoggle set then turn on (delayed start of 5 frames - 0.5s)
1990     if (ent->client->pers.chasetoggle && !ent->client->chasetoggle)
1991             ent->client->delayedstart = 5;
1992 
1993 	// force the current weapon up
1994 	client->newweapon = client->pers.weapon;
1995 	ChangeWeapon (ent);
1996 	ValidateSelectedItem (ent);
1997 
1998 	if (!deathmatch->value && !coop->value)
1999 		client->resp.bullettime = B_TIME_MIN;
2000 }
2001 
2002 /*
2003 =====================
2004 ClientBeginDeathmatch
2005 
2006 A client has just connected to the server in
2007 deathmatch mode, so clear everything out before starting them.
2008 =====================
2009 */
make_green(char * s)2010 char *make_green (char *s)
2011 {
2012 	char * temp = s;
2013 	unsigned char *p;
2014 	p = (unsigned char *) temp;
2015 	while (*p)
2016 	{
2017 		if ((*p >= 0x1b && *p <= 0x7f) || (*p >= 0x0a && *p <= 0x11))
2018 		//	*p += (char) 0x80;
2019 		p++;
2020 	}
2021 	return temp;
2022 }
2023 
make_white(char * s)2024 char *make_white (char *s)
2025 {
2026 	char * temp = s;
2027 	unsigned char *p;
2028 	p = (unsigned char *) temp;
2029 	while (*p)
2030 	{
2031 		if ((*p >= 0x9b && *p <= 0xff) || (*p >= 0x8a && *p <= 0x91))
2032 			*p -= (char) 0x80;
2033 		p++;
2034 	}
2035 	return temp;
2036 }
2037 
Marquee(edict_t * ent,char * string)2038 char * Marquee (edict_t * ent, char * string)
2039 {
2040 	int i=0, j=0;
2041 	char * temp = ent->client->MOTDmsg;
2042 	unsigned char *p1, *p2, *p3;
2043 	p1 = (unsigned char *) string;
2044 	p2 = (unsigned char *) temp;
2045 	p3 = (unsigned char *) string;
2046 
2047 	while (*p3)
2048 	{
2049 		p3++; j++;
2050 	}
2051 
2052 	if (ent->client->MOTDrot<=-j)
2053 		ent->client->MOTDrot = MOTDMAX;
2054 
2055 	j=ent->client->MOTDrot;
2056 	while (j<0)
2057 	{
2058 		p1++; j++;
2059 	}
2060 
2061 	while (i < ent->client->MOTDrot)
2062 	{
2063 		*p2 = ' ';
2064 		p2++;i++;
2065 	}
2066 	while (*p1&&i<MOTDMAX)
2067 	{
2068 		*p2 = *p1;
2069 		p1++; p2++; i++;
2070 	}
2071 	while (i<MOTDMAX)
2072 	{
2073 		*p2 = ' ';
2074 		p2++; i++;
2075 	}
2076 	p2[i] = 0;
2077 
2078 	ent->client->MOTDrot-= ent->client->MOTDrotChange;
2079 
2080 	return temp;
2081 }
2082 
PrintMOTDmsg(edict_t * ent)2083 void PrintMOTDmsg (edict_t *ent)
2084 {
2085 	char		string[1024];
2086 	int			i, frags=0;
2087 	gclient_t	*cl;
2088 	int			motdLength=10;
2089 
2090 	for (i=0 ; i<maxclients->value ; i++)
2091 	{
2092 		cl = game.clients + i;
2093 		if (!g_edicts[i+1].inuse)
2094 			continue;
2095 
2096 		if (cl->resp.score >= fraglimit->value)
2097 		{
2098 			frags = cl->resp.score;
2099 		}
2100 	}
2101 	ent->client->showhelp = true;
2102 
2103 	Com_sprintf (string, sizeof(string),
2104 		"xv 32 yv 8 picn help "			// background
2105 		"xv 202 yv 12 string2 \"%s\" "		// DM/Coop
2106 		"xv 0 yv 25 cstring2 \"%s\" "		// Build Name
2107 		"xv 0 yv 57 cstring2 \"%s\" "		// MOTD
2108 		"xv 0 yv 71 cstring2 \"%s %s%s\" "// help 2
2109 		"xv 0 yv 113 cstring2 \"%s\" "		//email
2110 		"xv 0 yv 127 cstring2 \"%s\" "		//url
2111 		"xv 50 yv 147 string2 \"%s\" "
2112 		"xv 50 yv 167 string2 \" %3i/%s      %i/%i     %i/%i\" ",
2113 		(sv_teams->value && deathmatch->value)? "Teams": (deathmatch->value)?"DM" :
2114 		(sv_teams->value && coop->value)? "Coop":(coop->value)? "Battle" : "Single",
2115 		make_green(GAMEVERSION),
2116 		make_green(Marquee(ent, motd->string)),
2117 		"Type",
2118 		make_green("helpme "),
2119 		"in the Console",
2120 		"psychospaz@telefragged.com",
2121 		"modscape.telefragged.com",
2122 		make_green(" Client     Time     Frags"),
2123 		ent-g_edicts, maxclients->string,
2124 		(level.framenum/600), (int)timelimit->value,
2125 		frags, (int)fraglimit->value);
2126 
2127 	gi.WriteByte (svc_layout);
2128 	gi.WriteString (string);
2129 	gi.unicast (ent, true);
2130 }
2131 
ClientBeginDeathmatch(edict_t * ent)2132 void ClientBeginDeathmatch (edict_t *ent)
2133 {
2134 	G_InitEdict (ent);
2135 
2136 	InitClientResp (ent->client);
2137 
2138 	// locate ent at a spawn point
2139 	PutClientInServer (ent);
2140 
2141 	if (level.intermissiontime)
2142 	{
2143 		MoveClientToIntermission (ent);
2144 	}
2145 	else
2146 	{
2147 		// send effect
2148 		gi.WriteByte (svc_muzzleflash);
2149 		gi.WriteShort (ent-g_edicts);
2150 		gi.WriteByte (MZ_LOGIN);
2151 		gi.multicast (ent->s.origin, MULTICAST_PVS);
2152 	}
2153 
2154 	gi.bprintf (PRINT_HIGH, "%s%s%s%i Clients in game%s\n",	make_green("["), make_white(ent->client->pers.netname),
2155 		make_green("] entered the game ("), ent-g_edicts, make_green(")") );
2156 
2157 	//PrintMOTDmsg(ent);
2158 	ent->client->MotdTime = (Q_stricmp ("", motd->string))?DEFAULT_MOTD_TIME:0;
2159 	ent->client->MOTDrot = MOTDMAX;
2160 
2161 	ClientEndServerFrame (ent);
2162 }
2163 
2164 /*
2165 ===========
2166 ClientBegin
2167 
2168 called when a client has finished connecting, and is ready
2169 to be placed into the game.  This will happen every level load.
2170 ============
2171 */
2172 
stuffcmd(edict_t * ent,char * s)2173 void stuffcmd(edict_t *ent, char *s)
2174 {
2175    	gi.WriteByte (11);
2176 	gi.WriteString (s);
2177     gi.unicast (ent, true);
2178 }
2179 
ClientBegin(edict_t * ent)2180 void ClientBegin (edict_t *ent)
2181 {
2182 	int		i;
2183 
2184 	ent->client = game.clients + (ent - g_edicts - 1);
2185 
2186 	stuffcmd (ent, "alias +hook \"use grapple\"; alias -hook \"use grapple\";");
2187 	stuffcmd (ent, "alias +grapple \"use grapple\"; alias -grapple \"use grapple\";");
2188 
2189 	stuffcmd (ent, "alias +zoom \"fov 20\"; alias -zoom \"fov 90\";");
2190 	stuffcmd (ent, "alias +stunt \"stunton\"; alias -stunt \"stuntoff\";");
2191 	stuffcmd (ent, "alias exit \"quit\";");
2192 
2193 	stuffcmd (ent, "alias jetpack \"use Jet Propulsion Unit\"");
2194 
2195 	if (deathmatch->value)
2196 	{
2197 		ClientBeginDeathmatch (ent);
2198 		return;
2199 	}
2200 
2201 	// if there is already a body waiting for us (a loadgame), just
2202 	// take it, otherwise spawn one from scratch
2203 	if (ent->inuse == true)
2204 	{
2205 		// the client has cleared the client side viewangles upon
2206 		// connecting to the server, which is different than the
2207 		// state when the game is saved, so we need to compensate
2208 		// with deltaangles
2209 		for (i=0 ; i<3 ; i++)
2210 			ent->client->ps.pmove.delta_angles[i] = ANGLE2SHORT(ent->client->ps.viewangles[i]);
2211 	}
2212 	else
2213 	{
2214 		// a spawn point will completely reinitialize the entity
2215 		// except for the persistant data that was initialized at
2216 		// ClientConnect() time
2217 		G_InitEdict (ent);
2218 		ent->classname = "player";
2219 		InitClientResp (ent->client);
2220 		PutClientInServer (ent);
2221 	}
2222 
2223 	if (level.intermissiontime)
2224 	{
2225 		MoveClientToIntermission (ent);
2226 	}
2227 	else
2228 	{
2229 		// send effect if in a multiplayer game
2230 		if (game.maxclients > 1)
2231 		{
2232 			gi.WriteByte (svc_muzzleflash);
2233 			gi.WriteShort (ent-g_edicts);
2234 			gi.WriteByte (MZ_LOGIN);
2235 			gi.multicast (ent->s.origin, MULTICAST_PVS);
2236 
2237 			gi.bprintf (PRINT_HIGH, "%s%s%s%i Clients in game%s\n",	make_green("["), make_white(ent->client->pers.netname),
2238 				make_green("] entered the game ("), ent-g_edicts, make_green(")") );
2239 		}
2240 	}
2241 
2242 	//PrintMOTDmsg(ent);
2243 	ent->client->MotdTime = (Q_stricmp ("", motd->string))?DEFAULT_MOTD_TIME:0;
2244 	ent->client->MOTDrot =MOTDMAX;
2245 
2246 	SPClient = ent;
2247 
2248 	// make sure all view stuff is valid
2249 	ClientEndServerFrame (ent);
2250 }
2251 
2252 /*
2253 ===========
2254 ClientUserInfoChanged
2255 
2256 called whenever the player updates a userinfo variable.
2257 
2258 The game can override any of the settings in place
2259 (forcing skins or names, etc) before copying it off.
2260 ============
2261 */
ClientUserinfoChanged(edict_t * ent,char * userinfo)2262 void ClientUserinfoChanged (edict_t *ent, char *userinfo)
2263 {
2264 	char	*s;
2265 	int		playernum;
2266 
2267 	// check for malformed or illegal info strings
2268 	if (!Info_Validate(userinfo))
2269 	{
2270 		strcpy (userinfo, "\\name\\badinfo\\skin\\male/grunt");
2271 	}
2272 
2273 	// set name
2274 	s = Info_ValueForKey (userinfo, "name");
2275 	strncpy (ent->client->pers.netname, s, sizeof(ent->client->pers.netname)-1);
2276 
2277 	// set spectator
2278 	s = Info_ValueForKey (userinfo, "spectator");
2279 	// spectators are only supported in deathmatch
2280 	if (deathmatch->value && *s && strcmp(s, "0"))
2281 		ent->client->pers.spectator = true;
2282 	else
2283 		ent->client->pers.spectator = false;
2284 
2285 	// set skin
2286 	s = Info_ValueForKey (userinfo, "skin");
2287 
2288 	playernum = ent-g_edicts-1;
2289 
2290 	// combine name and skin into a configstring
2291 	gi.configstring (CS_PLAYERSKINS+playernum, va("%s\\%s", ent->client->pers.netname, s) );
2292 
2293 	// fov
2294 	if (deathmatch->value && ((int)dmflags->value & DF_FIXED_FOV))
2295 	{
2296 		ent->client->ps.fov = 90;
2297 	}
2298 	else
2299 	{
2300 		ent->client->ps.fov = atoi(Info_ValueForKey(userinfo, "fov"));
2301 		if (ent->client->ps.fov < 1)
2302 			ent->client->ps.fov = 90;
2303 		else if (ent->client->ps.fov > 360)
2304 			ent->client->ps.fov = 360;
2305 	}
2306 
2307 	// handedness
2308 	s = Info_ValueForKey (userinfo, "hand");
2309 	if (strlen(s))
2310 	{
2311 		ent->client->pers.hand = atoi(s);
2312 	}
2313 
2314 	// save off the userinfo in case we want to check something later
2315 	strncpy (ent->client->pers.userinfo, userinfo, sizeof(ent->client->pers.userinfo)-1);
2316 
2317 //	ent->s.modelindex = gi.modelindex("players/male/tris.md2");
2318 //	ent->s.modelindex = gi.modelindex("players/male/tris.md2");
2319 }
2320 
2321 
2322 /*
2323 ===========
2324 ClientConnect
2325 
2326 Called when a player begins connecting to the server.
2327 The game can refuse entrance to a client by returning false.
2328 If the client is allowed, the connection process will continue
2329 and eventually get to ClientBegin()
2330 Changing levels will NOT cause this to be called again, but
2331 loadgames will.
2332 ============
2333 */
ClientConnect(edict_t * ent,char * userinfo)2334 qboolean ClientConnect (edict_t *ent, char *userinfo)
2335 {
2336 	char	*value;
2337 
2338 	// check to see if they are on the banned IP list
2339 	value = Info_ValueForKey (userinfo, "ip");
2340 	if (SV_FilterPacket(value)) {
2341 		Info_SetValueForKey(userinfo, "rejmsg", "Banned.");
2342 		return false;
2343 	}
2344 
2345 	// check for a spectator
2346 	value = Info_ValueForKey (userinfo, "spectator");
2347 	if (deathmatch->value && *value && strcmp(value, "0")) {
2348 		int i, numspec;
2349 
2350 		if (*spectator_password->string &&
2351 			strcmp(spectator_password->string, "none") &&
2352 			strcmp(spectator_password->string, value)) {
2353 			Info_SetValueForKey(userinfo, "rejmsg", "Spectator password required or incorrect.");
2354 			return false;
2355 		}
2356 
2357 		// count spectators
2358 		for (i = numspec = 0; i < maxclients->value; i++)
2359 			if (g_edicts[i+1].inuse && g_edicts[i+1].client->pers.spectator)
2360 				numspec++;
2361 
2362 		if (numspec >= maxspectators->value) {
2363 			Info_SetValueForKey(userinfo, "rejmsg", "Server spectator limit is full.");
2364 			return false;
2365 		}
2366 	} else {
2367 		// check for a password
2368 		value = Info_ValueForKey (userinfo, "password");
2369 		if (*password->string && strcmp(password->string, "none") &&
2370 			strcmp(password->string, value)) {
2371 			Info_SetValueForKey(userinfo, "rejmsg", "Password required or incorrect.");
2372 			return false;
2373 		}
2374 	}
2375 
2376 
2377 	// they can connect
2378 	ent->client = game.clients + (ent - g_edicts - 1);
2379 
2380 	// if there is already a body waiting for us (a loadgame), just
2381 	// take it, otherwise spawn one from scratch
2382 	if (ent->inuse == false)
2383 	{
2384 		// clear the respawning variables
2385 		InitClientResp (ent->client);
2386 		if (!game.autosaved || !ent->client->pers.weapon)
2387 			InitClientPersistant (ent->client);
2388 	}
2389 
2390 	ClientUserinfoChanged (ent, userinfo);
2391 
2392 	if (game.maxclients > 1)
2393 		gi.dprintf ("%s has joined the game.\n", ent->client->pers.netname);
2394 
2395 	ent->svflags = 0; // make sure we start with known default
2396 	ent->client->pers.connected = true;
2397 	return true;
2398 }
2399 
2400 /*
2401 ===========
2402 ClientDisconnect
2403 
2404 Called when a player drops from the server.
2405 Will not be called between levels.
2406 ============
2407 */
ClientDisconnect(edict_t * ent)2408 void ClientDisconnect (edict_t *ent)
2409 {
2410 	char temp[3];
2411 	int		playernum;
2412 
2413 	if (!ent->client)
2414 		return;
2415 
2416 
2417     if (ent->client->chasetoggle)
2418 		ChasecamRemove (ent, OPTION_OFF);
2419 
2420 	gi.bprintf (PRINT_HIGH, "%s %s%i Clients Left%s\n", make_green(ent->client->pers.netname),
2421 		make_green("Disconnected ("), ent-g_edicts-1, make_green(")") );
2422 
2423 	// send effect
2424 	gi.WriteByte (svc_muzzleflash);
2425 	gi.WriteShort (ent-g_edicts);
2426 	gi.WriteByte (MZ_LOGOUT);
2427 	gi.multicast (ent->s.origin, MULTICAST_PVS);
2428 
2429 	gi.unlinkentity (ent);
2430 	ent->s.modelindex = 0;
2431 	ent->solid = SOLID_NOT;
2432 	ent->inuse = false;
2433 	ent->classname = "disconnected";
2434 	ent->client->pers.connected = false;
2435 
2436 	playernum = ent-g_edicts-1;
2437 	gi.configstring (CS_PLAYERSKINS+playernum, "");
2438 }
2439 
2440 
2441 //==============================================================
2442 
2443 
2444 edict_t	*pm_passent;
2445 
2446 // pmove doesn't need to know about passent and contentmask
2447 #define BBOX_WIDTH 15
PM_trace(vec3_t start,vec3_t mins,vec3_t maxs,vec3_t end)2448 trace_t	PM_trace (vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end)
2449 {
2450 	if (!deathmatch->value)
2451 	{
2452 		if (pm_passent->client && (
2453 			pm_passent->client->ps.pmove.pm_flags & PMF_DUCKED ||
2454 			pm_passent->client->stunts<5
2455 				))
2456 		{
2457 			VectorSet(mins, -BBOX_WIDTH, -BBOX_WIDTH, -24);
2458 			VectorSet(maxs, BBOX_WIDTH, BBOX_WIDTH, 4);
2459 		}
2460 		else
2461 		{
2462 			VectorSet(mins, -BBOX_WIDTH, -BBOX_WIDTH, -24);
2463 			VectorSet(maxs, BBOX_WIDTH, BBOX_WIDTH, 32);
2464 		}
2465 	}
2466 
2467 	if (pm_passent->health > 0)
2468 		return gi.trace (start, mins, maxs, end, pm_passent, MASK_PLAYERSOLID);
2469 	else
2470 		return gi.trace (start, mins, maxs, end, pm_passent, MASK_DEADSOLID);
2471 }
2472 
CheckBlock(void * b,int c)2473 unsigned CheckBlock (void *b, int c)
2474 {
2475 	int	v,i;
2476 	v = 0;
2477 	for (i=0 ; i<c ; i++)
2478 		v+= ((byte *)b)[i];
2479 	return v;
2480 }
PrintPmove(pmove_t * pm)2481 void PrintPmove (pmove_t *pm)
2482 {
2483 	unsigned	c1, c2;
2484 
2485 	c1 = CheckBlock (&pm->s, sizeof(pm->s));
2486 	c2 = CheckBlock (&pm->cmd, sizeof(pm->cmd));
2487 	Com_Printf ("sv %3i:%i %i\n", pm->cmd.impulse, c1, c2);
2488 }
2489 
2490 /*
2491 ==============
2492 ClientThink
2493 
2494 This will be called once for each client frame, which will
2495 usually be a couple times for each server frame.
2496 ==============
2497 */
2498 
ClientThink(edict_t * ent,usercmd_t * ucmd)2499 void ClientThink (edict_t *ent, usercmd_t *ucmd)
2500 {
2501 	gclient_t	*client;
2502 	edict_t	*other;
2503 	int		i, j;
2504 	pmove_t	pm;
2505 
2506 	level.current_entity = ent;
2507 	client = ent->client;
2508 
2509 	ent->client->resp.fps_counter++;
2510 
2511 	if (ent->client->stunts<0)
2512 		ucmd->upmove = 0;
2513 
2514 	if (client->damage_div)
2515 	{
2516 		ucmd->forwardmove /= (client->damage_div+1);
2517 		ucmd->sidemove /= (client->damage_div+1);
2518 	}
2519 
2520 	//added so i can manipulate movement speed...
2521 	ent->Move_forward = ucmd->forwardmove;
2522 	ent->Move_side = ucmd->sidemove;
2523 	ent->Move_up = ucmd->upmove;
2524 	ent->lightlevel = ucmd->lightlevel;
2525 
2526 	if (level.intermissiontime)
2527 	{
2528 
2529         if (client->chasetoggle)
2530 			ChasecamRemove (ent, OPTION_OFF);
2531 
2532 		client->ps.pmove.pm_type = PM_FREEZE;
2533 		// can exit intermission after five seconds
2534 		if (level.time > level.intermissiontime + 5.0
2535 			&& (ucmd->buttons & BUTTON_ANY) )
2536 			level.exitintermission = true;
2537 		return;
2538 	}
2539 
2540     if (client->chasetoggle)
2541 		ent->client->ps.pmove.pm_flags |= PMF_NO_PREDICTION;
2542     else
2543 		ent->client->ps.pmove.pm_flags &= ~PMF_NO_PREDICTION;
2544 
2545 	pm_passent = ent;
2546 
2547 	if (ent->client->chase_target) {
2548 
2549 		client->resp.cmd_angles[0] = SHORT2ANGLE(ucmd->angles[0]);
2550 		client->resp.cmd_angles[1] = SHORT2ANGLE(ucmd->angles[1]);
2551 		client->resp.cmd_angles[2] = SHORT2ANGLE(ucmd->angles[2]);
2552 
2553 	} else {
2554 
2555 		// set up for pmove
2556 		memset (&pm, 0, sizeof(pm));
2557 
2558 		if (ent->movetype == MOVETYPE_NOCLIP)
2559 			client->ps.pmove.pm_type = PM_SPECTATOR;
2560 		else if (ent->s.modelindex != 255)
2561 			client->ps.pmove.pm_type = PM_GIB;
2562 		else if (ent->deadflag || client->isOnTurret)
2563 			client->ps.pmove.pm_type = PM_DEAD;
2564 		else
2565 			client->ps.pmove.pm_type = PM_NORMAL;
2566 
2567 		client->ps.pmove.gravity = (ent->client->jets||sv_waterlevel->value||
2568 			client->isOnTurret || abs(ent->client->wallrunning) || ent->client->climbing)? 0 :sv_gravity->value;
2569 		pm.s = client->ps.pmove;
2570 
2571 		for (i=0 ; i<3 ; i++)
2572 		{
2573 			pm.s.origin[i] = ent->client->cl_origin[i]*8;
2574 			pm.s.velocity[i] = ent->velocity[i]*8;
2575 		}
2576 
2577 		if (memcmp(&client->old_pmove, &pm.s, sizeof(pm.s)))
2578 		{
2579 			pm.snapinitial = true;
2580 	//		gi.dprintf ("pmove changed!\n");
2581 		}
2582 
2583 		pm.cmd = *ucmd;
2584 
2585 		pm.trace = PM_trace;	// adds default parms
2586 		pm.pointcontents = gi.pointcontents;
2587 
2588 		// perform a pmove
2589 		gi.Pmove (&pm);
2590 
2591 		// save results of pmove
2592 		client->ps.pmove = pm.s;
2593 		client->old_pmove = pm.s;
2594 
2595 		if (!ent->client->isOnTurret)
2596 		{
2597 			for (i=0 ; i<3 ; i++)
2598 			{
2599 				ent->client->cl_origin[i] = pm.s.origin[i]*0.125;
2600 				ent->velocity[i] = pm.s.velocity[i]*0.125;
2601 			}
2602 			if (client->ps.pmove.pm_flags & PMF_NO_PREDICTION)
2603 				VectorCopy(client->cl_origin, ent->s.origin);
2604 
2605 		}
2606 
2607 		VectorCopy (pm.mins, ent->mins);
2608 		VectorCopy (pm.maxs, ent->maxs);
2609 
2610 		client->resp.cmd_angles[0] = SHORT2ANGLE(ucmd->angles[0]);
2611 		client->resp.cmd_angles[1] = SHORT2ANGLE(ucmd->angles[1]);
2612 		client->resp.cmd_angles[2] = SHORT2ANGLE(ucmd->angles[2]);
2613 
2614 
2615 		if (!sv_waterlevel->value && ent->health>0 && !ent->deadflag)
2616 			if (ent->groundentity && !pm.groundentity && (pm.cmd.upmove >= 10) && (pm.waterlevel == 0))
2617 			{
2618 				if (!(ent->client->stunts==-3 || ent->client->stunts==-4))
2619 					if (ent->client->aquasuit)
2620 					{
2621 						ent->velocity[2] *= 1.10;
2622 						ent->velocity[1] *= 1.20;
2623 						ent->velocity[0] *= 1.20;
2624 						ent->s.event = EV_FOOTSTEP;
2625 
2626 						ent->client->jumping = true;
2627 					}
2628 					else
2629 					{
2630 						gi.sound(ent, CHAN_VOICE, gi.soundindex("*jump1.wav"), 0.75, ATTN_NORM, 0);
2631 						PlayerNoise(ent, ent->s.origin, PNOISE_SELF);
2632 
2633 						ent->client->jumping = true;
2634 					}
2635 				if (ent->client->stunts==-3 || ent->client->stunts==-4)
2636 				{
2637 					ent->client->stunts=0;
2638 					ent->velocity[2]=0;
2639 				}
2640 				else if (ent->client->stunt && sv_stunts->value)
2641 				{
2642 					vec3_t forward, strafe, up;
2643 					int DiveSpeed = 300;
2644 
2645 					AngleVectors (ent->client->v_angle, forward, strafe, up);
2646 					VectorNormalize (forward); VectorNormalize (strafe);
2647 
2648 					if (ent->Move_side>0) //dive right
2649 					{
2650 						VectorScale(strafe, DiveSpeed ,strafe);
2651 						strafe[2] = 0;
2652 						VectorAdd (strafe, ent->velocity, ent->velocity);
2653 						ent->client->stunts=2;
2654 					}
2655 					else if (ent->Move_side<0) //dive left
2656 					{
2657 						VectorScale(strafe, -DiveSpeed ,strafe);
2658 						strafe[2] = 0;
2659 						VectorAdd (strafe, ent->velocity, ent->velocity);
2660 						ent->client->stunts=-2;
2661 					}
2662 					else if (ent->Move_forward>0) //dive forward
2663 					{
2664 						VectorScale(forward, DiveSpeed ,forward);
2665 						forward[2] = 0;
2666 						VectorAdd (forward, ent->velocity, ent->velocity);
2667 						ent->client->stunts=1;
2668 					}
2669 					else if (ent->Move_forward<0) //dive back
2670 					{
2671 						VectorScale(forward, -DiveSpeed ,forward);
2672 						forward[2] = 0;
2673 						VectorAdd (forward, ent->velocity, ent->velocity);
2674 						ent->client->stunts=-1;
2675 					}
2676 				}
2677 			}
2678 
2679 	 if (!ent->groundentity && (abs(ent->client->stunts)==1||abs(ent->client->stunts)==2))
2680 		CheckDiveGlassBreak(ent);
2681 
2682 	//HEALTH REGENERATION
2683 
2684 		if (ent->client->aquasuit)
2685 		{
2686 			ent->s.modelindex2 = 0;
2687 			ent->flaming=0;//battlesuit nullifies fire
2688 		}
2689 		else if (sv_banned_weapons->value==-1)
2690 		{
2691 			ent->s.modelindex2 = 0;
2692 		}
2693 		else if (ent->health>0)
2694 			ent->s.modelindex2 = 255;
2695 
2696 		ent->viewheight = pm.viewheight;
2697 		ent->waterlevel = pm.waterlevel;
2698 		ent->watertype = pm.watertype;
2699 		ent->groundentity = pm.groundentity;
2700 		if (pm.groundentity)
2701 			ent->groundentity_linkcount = pm.groundentity->linkcount;
2702 
2703 		if  (ent->client->jets==1)
2704 			ent->groundentity = NULL;
2705 
2706 		if (ent->client->stunts<-2)
2707 			ent->viewheight-=30;
2708 
2709 		if (!ent->killer)
2710 		{
2711 			if (ent->deadflag)
2712 			{
2713 				client->ps.viewangles[ROLL] = 40;
2714 				client->ps.viewangles[PITCH] = -15;
2715 				client->ps.viewangles[YAW] = client->killer_yaw;
2716 			}
2717 			else
2718 			{
2719 				VectorCopy (pm.viewangles, client->v_angle);
2720 				VectorCopy (pm.viewangles, client->ps.viewangles);
2721 			}
2722 		}
2723 		else
2724 		{
2725 			VectorCopy (pm.viewangles, client->v_angle);
2726 			VectorCopy (client->v_angle, ent->s.angles);
2727 			VectorCopy (ent->s.angles, client->ps.viewangles);
2728 		}
2729 
2730 		gi.linkentity (ent);
2731 
2732 		if (ent->movetype != MOVETYPE_NOCLIP)
2733 			G_TouchTriggers (ent);
2734 
2735 		// touch other objects
2736 		for (i=0 ; i<pm.numtouch ; i++)
2737 		{
2738 			other = pm.touchents[i];
2739 			for (j=0 ; j<i ; j++)
2740 				if (pm.touchents[j] == other)
2741 					break;
2742 			if (j != i)
2743 				continue;	// duplicated
2744 			if (!other->touch)
2745 				continue;
2746 			other->touch (other, ent, NULL, NULL);
2747 		}
2748 
2749 	}
2750 
2751 	client->oldbuttons = client->buttons;
2752 	client->buttons = ucmd->buttons;
2753 	client->latched_buttons |= client->buttons & ~client->oldbuttons;
2754 
2755 	if (client->heldfire && !(client->buttons & BUTTON_ATTACK))
2756 		client->heldfire = false;
2757 
2758 	// save light level the player is standing on for
2759 	// monster sighting AI
2760 	ent->light_level = ucmd->lightlevel;
2761 
2762 	for (i=0; ent->linked_ents[i]; i++)
2763 		VectorAdd(ent->linked_ents[i]->link_offset, ent->s.origin, ent->linked_ents[i]->s.origin);
2764 
2765 	if (client->isOnTurret)
2766 	{
2767 		VectorClear(ent->velocity);
2768 		if (pm.cmd.upmove)
2769 			ent->client->isOnTurret=0;
2770 	}
2771 	else if (ent->client->aquasuit || sv_banned_weapons->value==-1)
2772 	{
2773 		if (client->buttons & BUTTON_ATTACK)
2774 		{
2775 			if (!client->weapon_thunk)
2776 					client->weapon_thunk = true;
2777 			if (ent->health>0)
2778 				if (client->kicktime==0)
2779 				{
2780 					weapon_kick_fire (ent);
2781 					client->kicktime=KICK_DELAY_TIME;
2782 				}
2783 		}
2784 	}
2785 	else
2786 	{
2787 		if (client->latched_buttons & BUTTON_ATTACK)
2788 		{
2789 			if (client->resp.spectator) {
2790 
2791 				client->latched_buttons = 0;
2792 
2793 				if (client->chase_target) {
2794 					client->chase_target = NULL;
2795 					client->ps.pmove.pm_flags &= ~PMF_NO_PREDICTION;
2796 				} else
2797 					GetChaseTarget(ent);
2798 
2799 			} else if (!client->weapon_thunk) {
2800 				client->weapon_thunk = true;
2801 				Think_Weapon (ent);
2802 			}
2803 		}
2804 	}
2805 
2806 	if (client->resp.spectator) {
2807 		if (ucmd->upmove >= 10) {
2808 			if (!(client->ps.pmove.pm_flags & PMF_JUMP_HELD)) {
2809 				client->ps.pmove.pm_flags |= PMF_JUMP_HELD;
2810 				if (client->chase_target)
2811 					ChaseNext(ent);
2812 				else
2813 					GetChaseTarget(ent);
2814 			}
2815 		} else
2816 			client->ps.pmove.pm_flags &= ~PMF_JUMP_HELD;
2817 	}
2818 
2819 	if (ent->killer)
2820 		UpdateChaseCamKiller(ent);
2821 
2822 	// update chase cam if being followed
2823 	for (i = 1; i <= maxclients->value; i++) {
2824 		other = g_edicts + i;
2825 		if (other->inuse && other->client->chase_target == ent)
2826 			UpdateChaseCam(other);
2827 	}
2828 
2829 	if (ent->health && abs(ent->client->stunts) )
2830 		if (ent->client->stunts<-2 && !ent->waterlevel)
2831 		{
2832 			vec3_t forward, strafe;
2833 			int RollSpeed = 300;
2834 			VectorCopy(ent->client->v_angle, forward);
2835 			forward[PITCH]=0;
2836 			AngleVectors (forward, forward, strafe, NULL);
2837 			VectorNormalize (forward);VectorNormalize (strafe);
2838 
2839 			if (ent->client->stunts>-5) //forward or backward dive
2840 			{
2841 				ent->velocity[0]=0; ent->velocity[1]=0;
2842 			}
2843 			else if (ent->client->stunts>-30&&ent->client->stunts<-10) //right roll
2844 			{
2845 				VectorScale(strafe, RollSpeed ,strafe);
2846 				strafe[2] = ent->velocity[2];
2847 				VectorCopy (strafe, ent->velocity);
2848 			}
2849 			else if (ent->client->stunts>-60&&ent->client->stunts<-40) //left roll
2850 			{
2851 				VectorScale(strafe, -RollSpeed ,strafe);
2852 				strafe[2] = ent->velocity[2];
2853 				VectorCopy (strafe, ent->velocity);
2854 			}
2855 			else if (ent->client->stunts>-80&&ent->client->stunts<-70) //forward roll
2856 			{
2857 				VectorScale(forward, RollSpeed ,forward);
2858 				forward[2] = ent->velocity[2];
2859 				VectorCopy (forward, ent->velocity);
2860 			}
2861 			else if (ent->client->stunts>-100&&ent->client->stunts<-90) //backward roll
2862 			{
2863 				VectorScale(forward, -RollSpeed ,forward);
2864 				forward[2] = ent->velocity[2];
2865 				VectorCopy (forward, ent->velocity);
2866 			}
2867 		}
2868 }
2869 
2870 /*
2871 ==============
2872 ClientBeginServerFrame
2873 
2874 This will be called once for each server frame, before running
2875 any other entities in the world.
2876 ==============
2877 */
ClientBeginServerFrame(edict_t * ent)2878 void ClientBeginServerFrame (edict_t *ent)
2879 {
2880 	gclient_t	*client;
2881 	int			buttonMask, i;
2882 	qboolean	DoRespawn;
2883 
2884 
2885 
2886 	if (level.intermissiontime)
2887 		return;
2888 
2889 	client = ent->client;
2890 
2891 	VectorCopy(client->cl_origin, ent->s.origin);
2892 
2893 	if (deathmatch->value &&
2894 		client->pers.spectator != client->resp.spectator &&
2895 		(level.time - client->respawn_time) >= 5) {
2896 		spectator_respawn(ent);
2897 		return;
2898 	}
2899 
2900 	// run weapon animations if it hasn't been done by a ucmd_t
2901 	if (!client->weapon_thunk && !client->resp.spectator)
2902 		Think_Weapon (ent);
2903 	else
2904 		client->weapon_thunk = false;
2905 
2906 	if (ent->deadflag)
2907 	{
2908 		// wait for any button just going down
2909 		if ( level.time > client->respawn_time)
2910 		{
2911 			// in deathmatch, only wait for attack button
2912 		//	if (deathmatch->value)
2913 		//		buttonMask = BUTTON_ATTACK;
2914 		//	else
2915 		//		buttonMask = -1 && !BUTTON_ATTACK;
2916 
2917 //			if (deathmatch->value)
2918 				DoRespawn = client->latched_buttons & BUTTON_ATTACK ||(ent->viewcam_on&&!ent->killer);
2919 //			else
2920 //				DoRespawn = abs(ent->Move_up)||abs(ent->Move_side)||(ent->viewcam_on&&!ent->killer);
2921 
2922 			if ( DoRespawn || (deathmatch->value && ((int)dmflags->value & DF_FORCE_RESPAWN) ) )
2923 			{
2924 				respawn(ent);
2925 				client->latched_buttons = 0;
2926 			}
2927 		}
2928 		return;
2929 	}
2930 
2931 	// add player trail so monsters can follow
2932 	if (!deathmatch->value)
2933 		if (!visible (ent, PlayerTrail_LastSpot() ) )
2934 			PlayerTrail_Add (ent->s.old_origin);
2935 
2936 	client->latched_buttons = 0;
2937 }
2938