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(edict_t * ent)171 void SP_info_player_intermission(edict_t *ent)
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.0f;
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 score on respawn
1060 ent->client->pers.score = ent->client->resp.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 = 0;
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 client->ps.pmove.delta_angles[i] = ANGLE2SHORT(spawn_angles[i] - client->resp.cmd_angles[i]);
1225
1226 ent->s.angles[PITCH] = 0;
1227 ent->s.angles[YAW] = spawn_angles[YAW];
1228 ent->s.angles[ROLL] = 0;
1229 VectorCopy (ent->s.angles, client->ps.viewangles);
1230 VectorCopy (ent->s.angles, client->v_angle);
1231
1232 // spawn a spectator
1233 if (client->pers.spectator) {
1234 client->chase_target = NULL;
1235
1236 client->resp.spectator = true;
1237
1238 ent->movetype = MOVETYPE_NOCLIP;
1239 ent->solid = SOLID_NOT;
1240 ent->svflags |= SVF_NOCLIENT;
1241 ent->client->ps.gunindex = 0;
1242 gi.linkentity (ent);
1243 return;
1244 } else
1245 client->resp.spectator = false;
1246
1247 if (!KillBox (ent))
1248 { // could't spawn in?
1249 }
1250
1251 gi.linkentity (ent);
1252
1253 // force the current weapon up
1254 client->newweapon = client->pers.weapon;
1255 ChangeWeapon (ent);
1256 }
1257
1258 /*
1259 =====================
1260 ClientBeginDeathmatch
1261
1262 A client has just connected to the server in
1263 deathmatch mode, so clear everything out before starting them.
1264 =====================
1265 */
ClientBeginDeathmatch(edict_t * ent)1266 void ClientBeginDeathmatch (edict_t *ent)
1267 {
1268 G_InitEdict (ent);
1269
1270 InitClientResp (ent->client);
1271
1272 // locate ent at a spawn point
1273 PutClientInServer (ent);
1274
1275 // send effect
1276 gi.WriteByte (svc_muzzleflash);
1277 gi.WriteShort (ent-g_edicts);
1278 gi.WriteByte (MZ_LOGIN);
1279 gi.multicast (ent->s.origin, MULTICAST_PVS);
1280
1281 gi.bprintf (PRINT_HIGH, "%s entered the game\n", ent->client->pers.netname);
1282
1283 // make sure all view stuff is valid
1284 ClientEndServerFrame (ent);
1285 }
1286
1287
1288 /*
1289 ===========
1290 ClientBegin
1291
1292 called when a client has finished connecting, and is ready
1293 to be placed into the game. This will happen every level load.
1294 ============
1295 */
ClientBegin(edict_t * ent)1296 void ClientBegin (edict_t *ent)
1297 {
1298 int i;
1299
1300 ent->client = game.clients + (ent - g_edicts - 1);
1301
1302 if (deathmatch->value)
1303 {
1304 ClientBeginDeathmatch (ent);
1305 return;
1306 }
1307
1308 // if there is already a body waiting for us (a loadgame), just
1309 // take it, otherwise spawn one from scratch
1310 if (ent->inuse == true)
1311 {
1312 // the client has cleared the client side viewangles upon
1313 // connecting to the server, which is different than the
1314 // state when the game is saved, so we need to compensate
1315 // with deltaangles
1316 for (i=0 ; i<3 ; i++)
1317 ent->client->ps.pmove.delta_angles[i] = ANGLE2SHORT(ent->client->ps.viewangles[i]);
1318 }
1319 else
1320 {
1321 // a spawn point will completely reinitialize the entity
1322 // except for the persistant data that was initialized at
1323 // ClientConnect() time
1324 G_InitEdict (ent);
1325 ent->classname = "player";
1326 InitClientResp (ent->client);
1327 PutClientInServer (ent);
1328 }
1329
1330 if (level.intermissiontime)
1331 {
1332 MoveClientToIntermission (ent);
1333 }
1334 else
1335 {
1336 // send effect if in a multiplayer game
1337 if (game.maxclients > 1)
1338 {
1339 gi.WriteByte (svc_muzzleflash);
1340 gi.WriteShort (ent-g_edicts);
1341 gi.WriteByte (MZ_LOGIN);
1342 gi.multicast (ent->s.origin, MULTICAST_PVS);
1343
1344 gi.bprintf (PRINT_HIGH, "%s entered the game\n", ent->client->pers.netname);
1345 }
1346 }
1347
1348 // make sure all view stuff is valid
1349 ClientEndServerFrame (ent);
1350 }
1351
1352 /*
1353 ===========
1354 ClientUserInfoChanged
1355
1356 called whenever the player updates a userinfo variable.
1357
1358 The game can override any of the settings in place
1359 (forcing skins or names, etc) before copying it off.
1360 ============
1361 */
ClientUserinfoChanged(edict_t * ent,char * userinfo)1362 void ClientUserinfoChanged (edict_t *ent, char *userinfo)
1363 {
1364 char *s;
1365 int playernum;
1366
1367 // check for malformed or illegal info strings
1368 if (!Info_Validate(userinfo))
1369 {
1370 strcpy (userinfo, "\\name\\badinfo\\skin\\male/grunt");
1371 }
1372
1373 // set name
1374 s = Info_ValueForKey (userinfo, "name");
1375 strncpy (ent->client->pers.netname, s, sizeof(ent->client->pers.netname)-1);
1376
1377 // set spectator
1378 s = Info_ValueForKey (userinfo, "spectator");
1379 // spectators are only supported in deathmatch
1380 if (deathmatch->value && *s && strcmp(s, "0"))
1381 ent->client->pers.spectator = true;
1382 else
1383 ent->client->pers.spectator = false;
1384
1385 // set skin
1386 s = Info_ValueForKey (userinfo, "skin");
1387
1388 playernum = ent-g_edicts-1;
1389
1390 // combine name and skin into a configstring
1391 gi.configstring (CS_PLAYERSKINS+playernum, va("%s\\%s", ent->client->pers.netname, s) );
1392
1393 // fov
1394 if (deathmatch->value && ((int)dmflags->value & DF_FIXED_FOV))
1395 {
1396 ent->client->ps.fov = 90;
1397 }
1398 else
1399 {
1400 ent->client->ps.fov = atoi(Info_ValueForKey(userinfo, "fov"));
1401 if (ent->client->ps.fov < 1)
1402 ent->client->ps.fov = 90;
1403 else if (ent->client->ps.fov > 160)
1404 ent->client->ps.fov = 160;
1405 }
1406
1407 // handedness
1408 s = Info_ValueForKey (userinfo, "hand");
1409 if (strlen(s))
1410 {
1411 ent->client->pers.hand = atoi(s);
1412 }
1413
1414 // save off the userinfo in case we want to check something later
1415 strncpy (ent->client->pers.userinfo, userinfo, sizeof(ent->client->pers.userinfo)-1);
1416 }
1417
1418
1419 /*
1420 ===========
1421 ClientConnect
1422
1423 Called when a player begins connecting to the server.
1424 The game can refuse entrance to a client by returning false.
1425 If the client is allowed, the connection process will continue
1426 and eventually get to ClientBegin()
1427 Changing levels will NOT cause this to be called again, but
1428 loadgames will.
1429 ============
1430 */
ClientConnect(edict_t * ent,char * userinfo)1431 qboolean ClientConnect (edict_t *ent, char *userinfo)
1432 {
1433 char *value;
1434
1435 // check to see if they are on the banned IP list
1436 value = Info_ValueForKey (userinfo, "ip");
1437 if (SV_FilterPacket(value)) {
1438 Info_SetValueForKey(userinfo, "rejmsg", "Banned.");
1439 return false;
1440 }
1441
1442 // check for a spectator
1443 value = Info_ValueForKey (userinfo, "spectator");
1444 if (deathmatch->value && *value && strcmp(value, "0")) {
1445 int i, numspec;
1446
1447 if (*spectator_password->string &&
1448 strcmp(spectator_password->string, "none") &&
1449 strcmp(spectator_password->string, value)) {
1450 Info_SetValueForKey(userinfo, "rejmsg", "Spectator password required or incorrect.");
1451 return false;
1452 }
1453
1454 // count spectators
1455 for (i = numspec = 0; i < maxclients->value; i++)
1456 if (g_edicts[i+1].inuse && g_edicts[i+1].client->pers.spectator)
1457 numspec++;
1458
1459 if (numspec >= maxspectators->value) {
1460 Info_SetValueForKey(userinfo, "rejmsg", "Server spectator limit is full.");
1461 return false;
1462 }
1463 } else {
1464 // check for a password
1465 value = Info_ValueForKey (userinfo, "password");
1466 if (*password->string && strcmp(password->string, "none") &&
1467 strcmp(password->string, value)) {
1468 Info_SetValueForKey(userinfo, "rejmsg", "Password required or incorrect.");
1469 return false;
1470 }
1471 }
1472
1473
1474 // they can connect
1475 ent->client = game.clients + (ent - g_edicts - 1);
1476
1477 // if there is already a body waiting for us (a loadgame), just
1478 // take it, otherwise spawn one from scratch
1479 if (ent->inuse == false)
1480 {
1481 // clear the respawning variables
1482 InitClientResp (ent->client);
1483 if (!game.autosaved || !ent->client->pers.weapon)
1484 InitClientPersistant (ent->client);
1485 }
1486
1487 ClientUserinfoChanged (ent, userinfo);
1488
1489 if (game.maxclients > 1)
1490 gi.dprintf ("%s connected\n", ent->client->pers.netname);
1491
1492 ent->client->pers.connected = true;
1493 return true;
1494 }
1495
1496 /*
1497 ===========
1498 ClientDisconnect
1499
1500 Called when a player drops from the server.
1501 Will not be called between levels.
1502 ============
1503 */
ClientDisconnect(edict_t * ent)1504 void ClientDisconnect (edict_t *ent)
1505 {
1506 int playernum;
1507
1508 if (!ent->client)
1509 return;
1510
1511 gi.bprintf (PRINT_HIGH, "%s disconnected\n", ent->client->pers.netname);
1512
1513 // send effect
1514 gi.WriteByte (svc_muzzleflash);
1515 gi.WriteShort (ent-g_edicts);
1516 gi.WriteByte (MZ_LOGOUT);
1517 gi.multicast (ent->s.origin, MULTICAST_PVS);
1518
1519 gi.unlinkentity (ent);
1520 ent->s.modelindex = 0;
1521 ent->solid = SOLID_NOT;
1522 ent->inuse = false;
1523 ent->classname = "disconnected";
1524 ent->client->pers.connected = false;
1525
1526 playernum = ent-g_edicts-1;
1527 gi.configstring (CS_PLAYERSKINS+playernum, "");
1528 }
1529
1530
1531 //==============================================================
1532
1533
1534 edict_t *pm_passent;
1535
1536 // pmove doesn't need to know about passent and contentmask
PM_trace(vec3_t start,vec3_t mins,vec3_t maxs,vec3_t end)1537 trace_t PM_trace (vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end)
1538 {
1539 if (pm_passent->health > 0)
1540 return gi.trace (start, mins, maxs, end, pm_passent, MASK_PLAYERSOLID);
1541 else
1542 return gi.trace (start, mins, maxs, end, pm_passent, MASK_DEADSOLID);
1543 }
1544
CheckBlock(void * b,int c)1545 unsigned CheckBlock (void *b, int c)
1546 {
1547 int v,i;
1548 v = 0;
1549 for (i=0 ; i<c ; i++)
1550 v+= ((byte *)b)[i];
1551 return v;
1552 }
PrintPmove(pmove_t * pm)1553 void PrintPmove (pmove_t *pm)
1554 {
1555 unsigned c1, c2;
1556
1557 c1 = CheckBlock (&pm->s, sizeof(pm->s));
1558 c2 = CheckBlock (&pm->cmd, sizeof(pm->cmd));
1559 gi.dprintf ("sv %3i:%i %i\n", pm->cmd.impulse, c1, c2);
1560 }
1561
1562 /*
1563 ==============
1564 ClientThink
1565
1566 This will be called once for each client frame, which will
1567 usually be a couple times for each server frame.
1568 ==============
1569 */
ClientThink(edict_t * ent,usercmd_t * ucmd)1570 void ClientThink (edict_t *ent, usercmd_t *ucmd)
1571 {
1572 gclient_t *client;
1573 edict_t *other;
1574 int i, j;
1575 pmove_t pm;
1576
1577 level.current_entity = ent;
1578 client = ent->client;
1579
1580 if (level.intermissiontime)
1581 {
1582 client->ps.pmove.pm_type = PM_FREEZE;
1583 // can exit intermission after five seconds
1584 if (level.time > level.intermissiontime + 5.0f
1585 && (ucmd->buttons & BUTTON_ANY) )
1586 level.exitintermission = true;
1587 return;
1588 }
1589
1590 pm_passent = ent;
1591
1592 if (ent->client->chase_target) {
1593
1594 client->resp.cmd_angles[0] = SHORT2ANGLE(ucmd->angles[0]);
1595 client->resp.cmd_angles[1] = SHORT2ANGLE(ucmd->angles[1]);
1596 client->resp.cmd_angles[2] = SHORT2ANGLE(ucmd->angles[2]);
1597
1598 } else {
1599
1600 // set up for pmove
1601 memset (&pm, 0, sizeof(pm));
1602
1603 if (ent->movetype == MOVETYPE_NOCLIP)
1604 client->ps.pmove.pm_type = PM_SPECTATOR;
1605 else if (ent->s.modelindex != 255)
1606 client->ps.pmove.pm_type = PM_GIB;
1607 else if (ent->deadflag)
1608 client->ps.pmove.pm_type = PM_DEAD;
1609 else
1610 client->ps.pmove.pm_type = PM_NORMAL;
1611
1612 client->ps.pmove.gravity = sv_gravity->value;
1613 pm.s = client->ps.pmove;
1614
1615 for (i=0 ; i<3 ; i++)
1616 {
1617 pm.s.origin[i] = ent->s.origin[i]*8;
1618 pm.s.velocity[i] = ent->velocity[i]*8;
1619 }
1620
1621 if (memcmp(&client->old_pmove, &pm.s, sizeof(pm.s)))
1622 {
1623 pm.snapinitial = true;
1624 // gi.dprintf ("pmove changed!\n");
1625 }
1626
1627 pm.cmd = *ucmd;
1628
1629 pm.trace = PM_trace; // adds default parms
1630 pm.pointcontents = gi.pointcontents;
1631
1632 // perform a pmove
1633 gi.Pmove (&pm);
1634
1635 // save results of pmove
1636 client->ps.pmove = pm.s;
1637 client->old_pmove = pm.s;
1638
1639 for (i=0 ; i<3 ; i++)
1640 {
1641 ent->s.origin[i] = pm.s.origin[i]*0.125;
1642 ent->velocity[i] = pm.s.velocity[i]*0.125;
1643 }
1644
1645 VectorCopy (pm.mins, ent->mins);
1646 VectorCopy (pm.maxs, ent->maxs);
1647
1648 client->resp.cmd_angles[0] = SHORT2ANGLE(ucmd->angles[0]);
1649 client->resp.cmd_angles[1] = SHORT2ANGLE(ucmd->angles[1]);
1650 client->resp.cmd_angles[2] = SHORT2ANGLE(ucmd->angles[2]);
1651
1652 if (ent->groundentity && !pm.groundentity && (pm.cmd.upmove >= 10) && (pm.waterlevel == 0))
1653 {
1654 gi.sound(ent, CHAN_VOICE, gi.soundindex("*jump1.wav"), 1, ATTN_NORM, 0);
1655 PlayerNoise(ent, ent->s.origin, PNOISE_SELF);
1656 }
1657
1658 ent->viewheight = pm.viewheight;
1659 ent->waterlevel = pm.waterlevel;
1660 ent->watertype = pm.watertype;
1661 ent->groundentity = pm.groundentity;
1662 if (pm.groundentity)
1663 ent->groundentity_linkcount = pm.groundentity->linkcount;
1664
1665 if (ent->deadflag)
1666 {
1667 client->ps.viewangles[ROLL] = 40;
1668 client->ps.viewangles[PITCH] = -15;
1669 client->ps.viewangles[YAW] = client->killer_yaw;
1670 }
1671 else
1672 {
1673 VectorCopy (pm.viewangles, client->v_angle);
1674 VectorCopy (pm.viewangles, client->ps.viewangles);
1675 }
1676
1677 gi.linkentity (ent);
1678
1679 if (ent->movetype != MOVETYPE_NOCLIP)
1680 G_TouchTriggers (ent);
1681
1682 // touch other objects
1683 for (i=0 ; i<pm.numtouch ; i++)
1684 {
1685 other = pm.touchents[i];
1686 for (j=0 ; j<i ; j++)
1687 if (pm.touchents[j] == other)
1688 break;
1689 if (j != i)
1690 continue; // duplicated
1691 if (!other->touch)
1692 continue;
1693 other->touch (other, ent, NULL, NULL);
1694 }
1695
1696 }
1697
1698 client->oldbuttons = client->buttons;
1699 client->buttons = ucmd->buttons;
1700 client->latched_buttons |= client->buttons & ~client->oldbuttons;
1701
1702 // save light level the player is standing on for
1703 // monster sighting AI
1704 ent->light_level = ucmd->lightlevel;
1705
1706 // fire weapon from final position if needed
1707 if (client->latched_buttons & BUTTON_ATTACK)
1708 {
1709 if (client->resp.spectator) {
1710
1711 client->latched_buttons = 0;
1712
1713 if (client->chase_target) {
1714 client->chase_target = NULL;
1715 client->ps.pmove.pm_flags &= ~PMF_NO_PREDICTION;
1716 } else
1717 GetChaseTarget(ent);
1718
1719 } else if (!client->weapon_thunk) {
1720 client->weapon_thunk = true;
1721 Think_Weapon (ent);
1722 }
1723 }
1724
1725 if (client->resp.spectator) {
1726 if (ucmd->upmove >= 10) {
1727 if (!(client->ps.pmove.pm_flags & PMF_JUMP_HELD)) {
1728 client->ps.pmove.pm_flags |= PMF_JUMP_HELD;
1729 if (client->chase_target)
1730 ChaseNext(ent);
1731 else
1732 GetChaseTarget(ent);
1733 }
1734 } else
1735 client->ps.pmove.pm_flags &= ~PMF_JUMP_HELD;
1736 }
1737
1738 // update chase cam if being followed
1739 for (i = 1; i <= maxclients->value; i++) {
1740 other = g_edicts + i;
1741 if (other->inuse && other->client->chase_target == ent)
1742 UpdateChaseCam(other);
1743 }
1744 }
1745
1746
1747 /*
1748 ==============
1749 ClientBeginServerFrame
1750
1751 This will be called once for each server frame, before running
1752 any other entities in the world.
1753 ==============
1754 */
ClientBeginServerFrame(edict_t * ent)1755 void ClientBeginServerFrame (edict_t *ent)
1756 {
1757 gclient_t *client;
1758 int buttonMask;
1759
1760 if (level.intermissiontime)
1761 return;
1762
1763 client = ent->client;
1764
1765 if (deathmatch->value &&
1766 client->pers.spectator != client->resp.spectator &&
1767 (level.time - client->respawn_time) >= 5) {
1768 spectator_respawn(ent);
1769 return;
1770 }
1771
1772 // run weapon animations if it hasn't been done by a ucmd_t
1773 if (!client->weapon_thunk && !client->resp.spectator)
1774 Think_Weapon (ent);
1775 else
1776 client->weapon_thunk = false;
1777
1778 if (ent->deadflag)
1779 {
1780 // wait for any button just going down
1781 if ( level.time > client->respawn_time)
1782 {
1783 // in deathmatch, only wait for attack button
1784 if (deathmatch->value)
1785 buttonMask = BUTTON_ATTACK;
1786 else
1787 buttonMask = -1;
1788
1789 if ( ( client->latched_buttons & buttonMask ) ||
1790 (deathmatch->value && ((int)dmflags->value & DF_FORCE_RESPAWN) ) )
1791 {
1792 respawn(ent);
1793 client->latched_buttons = 0;
1794 }
1795 }
1796 return;
1797 }
1798
1799 // add player trail so monsters can follow
1800 if (!deathmatch->value)
1801 if (!visible (ent, PlayerTrail_LastSpot() ) )
1802 PlayerTrail_Add (ent->s.old_origin);
1803
1804 client->latched_buttons = 0;
1805 }
1806