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