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