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