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