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