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