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