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