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