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