1 #include "g_local.h"
2 #include "m_player.h"
3
4 #define KICK_DELAY_TIME 2 //server frames (.1 sec each)
5 #define DEFAULT_MOTD_TIME 15 //seconds itll remain
6 #define MOTDMAX 26
7
8 void ClientUserinfoChanged (edict_t *ent, char *userinfo);
9 void CopyToBodyQue (edict_t *ent);
10 void SP_misc_teleporter_dest (edict_t *ent);
11 edict_t * MakeNewBody (edict_t *ent);
12 edict_t * ThrowClientHeadNew (edict_t *self);
13
14
15 //
16 // Gross, ugly, disgustuing hack section
17 //
18
19 // this function is an ugly as hell hack to fix some map flaws
20 //
21 // the coop spawn spots on some maps are SNAFU. There are coop spots
22 // with the wrong targetname as well as spots with no name at all
23 //
24 // we use carnal knowledge of the maps to fix the coop spot targetnames to match
25 // that of the nearest named single player spot
26
SP_FixCoopSpots(edict_t * self)27 static void SP_FixCoopSpots (edict_t *self)
28 {
29 edict_t *spot;
30 vec3_t d;
31
32 spot = NULL;
33
34 while(1)
35 {
36 spot = G_Find(spot, FOFS(classname), "info_player_start");
37 if (!spot)
38 return;
39 if (!spot->targetname)
40 continue;
41 VectorSubtract(self->s.origin, spot->s.origin, d);
42 if (VectorLength(d) < 384)
43 {
44 if ((!self->targetname) || Q_stricmp(self->targetname, spot->targetname) != 0)
45 {
46 // gi.dprintf("FixCoopSpots changed %s at %s targetname from %s to %s\n", self->classname, vtos(self->s.origin), self->targetname, spot->targetname);
47 self->targetname = spot->targetname;
48 }
49 return;
50 }
51 }
52 }
53
54 // now if that one wasn't ugly enough for you then try this one on for size
55 // some maps don't have any coop spots at all, so we need to create them
56 // where they should have been
57
SP_CreateCoopSpots(edict_t * self)58 static void SP_CreateCoopSpots (edict_t *self)
59 {
60 edict_t *spot;
61
62 if(Q_stricmp(level.mapname, "security") == 0)
63 {
64 spot = G_Spawn();
65 spot->classname = "info_player_coop";
66 spot->s.origin[0] = 188 - 64;
67 spot->s.origin[1] = -164;
68 spot->s.origin[2] = 80;
69 spot->targetname = "jail3";
70 spot->s.angles[1] = 90;
71
72 spot = G_Spawn();
73 spot->classname = "info_player_coop";
74 spot->s.origin[0] = 188 + 64;
75 spot->s.origin[1] = -164;
76 spot->s.origin[2] = 80;
77 spot->targetname = "jail3";
78 spot->s.angles[1] = 90;
79
80 spot = G_Spawn();
81 spot->classname = "info_player_coop";
82 spot->s.origin[0] = 188 + 128;
83 spot->s.origin[1] = -164;
84 spot->s.origin[2] = 80;
85 spot->targetname = "jail3";
86 spot->s.angles[1] = 90;
87
88 return;
89 }
90 }
91
92 //void SP_LensFlare (edict_t *flare)
93 //void SP_SunFlare (edict_t *flare)
94
SP_AddSunFlares(edict_t * self)95 static void SP_AddSunFlares (edict_t *self)
96 {
97 edict_t *newent;
98
99 if(Q_stricmp(level.mapname, "base1") == 0)
100 {
101 newent = G_Spawn();
102 newent->s.origin[0] = -155;
103 newent->s.origin[1] = 877;
104 newent->s.origin[2] = 441;
105 SP_SunFlare (newent);
106 return;
107 }
108 }
109
110 /*QUAKED info_player_start (1 0 0) (-16 -16 -24) (16 16 32)
111 The normal starting point for a level.
112 */
SP_info_player_start(edict_t * self)113 void SP_info_player_start(edict_t *self)
114 {
115 if (!coop->value && !deathmatch->value)
116 {
117 self->think = SP_AddSunFlares;
118 self->nextthink = level.time + FRAMETIME;
119 return;
120 }
121 if (!coop->value)
122 return;
123 if(Q_stricmp(level.mapname, "security") == 0)
124 {
125 // invoke one of our gross, ugly, disgusting hacks
126 self->think = SP_CreateCoopSpots;
127 self->nextthink = level.time + FRAMETIME;
128 }
129 }
130
131 /*QUAKED info_player_deathmatch (1 0 1) (-16 -16 -24) (16 16 32)
132 potential spawning position for deathmatch games
133 */
SP_info_player_deathmatch(edict_t * self)134 void SP_info_player_deathmatch(edict_t *self)
135 {
136 if (!deathmatch->value)
137 {
138 G_FreeEdict (self);
139 return;
140 }
141 SP_misc_spawn_dest (self);
142 }
143
144 /*QUAKED info_player_coop (1 0 1) (-16 -16 -24) (16 16 32)
145 potential spawning position for coop games
146 */
147
SP_info_player_coop(edict_t * self)148 void SP_info_player_coop(edict_t *self)
149 {
150 if (!coop->value)
151 {
152 G_FreeEdict (self);
153 return;
154 }
155
156 if((Q_stricmp(level.mapname, "jail2") == 0) ||
157 (Q_stricmp(level.mapname, "jail4") == 0) ||
158 (Q_stricmp(level.mapname, "mine1") == 0) ||
159 (Q_stricmp(level.mapname, "mine2") == 0) ||
160 (Q_stricmp(level.mapname, "mine3") == 0) ||
161 (Q_stricmp(level.mapname, "mine4") == 0) ||
162 (Q_stricmp(level.mapname, "lab") == 0) ||
163 (Q_stricmp(level.mapname, "boss1") == 0) ||
164 (Q_stricmp(level.mapname, "fact3") == 0) ||
165 (Q_stricmp(level.mapname, "biggun") == 0) ||
166 (Q_stricmp(level.mapname, "space") == 0) ||
167 (Q_stricmp(level.mapname, "command") == 0) ||
168 (Q_stricmp(level.mapname, "power2") == 0) ||
169 (Q_stricmp(level.mapname, "strike") == 0))
170 {
171 // invoke one of our gross, ugly, disgusting hacks
172 self->think = SP_FixCoopSpots;
173 self->nextthink = level.time + FRAMETIME;
174 }
175 }
176
177
178 /*QUAKED info_player_intermission (1 0 1) (-16 -16 -24) (16 16 32)
179 The deathmatch intermission point will be at one of these
180 Use 'angles' instead of 'angle', so you can set pitch or roll as well as yaw. 'pitch yaw roll'
181 */
SP_info_player_intermission(void)182 void SP_info_player_intermission(void)
183 {
184 }
185
186
187 //=======================================================================
188
189
player_pain(edict_t * self,edict_t * other,float kick,int damage)190 void player_pain (edict_t *self, edict_t *other, float kick, int damage)
191 {
192 // player pain is handled at the end of the frame in P_DamageFeedback
193 }
194
195
IsFemale(edict_t * ent)196 qboolean IsFemale (edict_t *ent)
197 {
198 char *info;
199
200 if (!ent->client)
201 return false;
202
203 info = Info_ValueForKey (ent->client->pers.userinfo, "gender");
204 if (info[0] == 'f' || info[0] == 'F')
205 return true;
206 return false;
207 }
208
IsNeutral(edict_t * ent)209 qboolean IsNeutral (edict_t *ent)
210 {
211 char *info;
212
213 if (!ent->client)
214 return false;
215
216 info = Info_ValueForKey (ent->client->pers.userinfo, "gender");
217 if (info[0] != 'f' && info[0] != 'F' && info[0] != 'm' && info[0] != 'M')
218 return true;
219 return false;
220 }
221
ClientObituary(edict_t * self,edict_t * inflictor,edict_t * attacker)222 void ClientObituary (edict_t *self, edict_t *inflictor, edict_t *attacker)
223 {
224 int mod;
225 char *message;
226 char *message2;
227 qboolean ff, headshot;
228
229 if (coop->value && attacker->client)
230 meansOfDeath |= MOD_FRIENDLY_FIRE;
231
232 if (deathmatch->value || coop->value || true)
233 {
234 ff = meansOfDeath & MOD_FRIENDLY_FIRE;
235 mod = meansOfDeath & ~MOD_FRIENDLY_FIRE & ~MOD_HEAD;
236 message = NULL;
237 message2 = "";
238 /*
239 if (headShot)
240 gi.centerprintf (self, make_green("(HeadshoT)"));
241 else
242 gi.centerprintf (self, make_green("(BodyshoT)"));//*/
243
244 switch (mod)
245 {
246 case MOD_SUICIDE:
247 message = "suicides";
248 break;
249 case MOD_FALLING:
250 message = "cratered";
251 break;
252 case MOD_CRUSH:
253 message = "was squished";
254 break;
255 case MOD_WATER:
256 message = "sank like a rock";
257 break;
258 case MOD_SLIME:
259 message = "melted";
260 break;
261 case MOD_LAVA:
262 message = "does a back flip into the lava";
263 break;
264 case MOD_EXPLOSIVE:
265 case MOD_BARREL:
266 message = "blew up";
267 break;
268 case MOD_FLAME:
269 message = "was torched";
270 break;
271 case MOD_EXIT:
272 message = "found a way out";
273 break;
274 case MOD_TARGET_LASER:
275 message = "saw the light";
276 break;
277 case MOD_DISCHARGE:
278 message = "felt the discharge";
279 break;
280 case MOD_TARGET_BLASTER:
281 message = "got blasted";
282 break;
283 case MOD_BOMB:
284 case MOD_SPLASH:
285 case MOD_TRIGGER_HURT:
286 message = "was in the wrong place";
287 break;
288 }
289 if (attacker == self)
290 {
291 switch (mod)
292 {
293 case MOD_HELD_GRENADE:
294 message = "tried to put the pin back in";
295 break;
296 case MOD_FIRE:
297 if (IsNeutral(self))
298 message = "set itself aflame";
299 else if (IsFemale(self))
300 message = "set herself aflame";
301 else
302 message = "set himself aflame";
303 break;
304 case MOD_HG_SPLASH:
305 case MOD_G_SPLASH:
306 if (IsNeutral(self))
307 message = "tripped on its own grenade";
308 else if (IsFemale(self))
309 message = "tripped on her own grenade";
310 else
311 message = "tripped on his own grenade";
312 break;
313 case MOD_R_SPLASH:
314 if (IsNeutral(self))
315 message = "blew itself up";
316 else if (IsFemale(self))
317 message = "blew herself up";
318 else
319 message = "blew himself up";
320 break;
321 case MOD_BFG_BLAST:
322 message = "should have used a smaller gun";
323 break;
324 default:
325 if (IsNeutral(self))
326 message = "killed itself";
327 else if (IsFemale(self))
328 message = "killed herself";
329 else
330 message = "killed himself";
331 break;
332 }
333 }
334 if (message)
335 {
336 gi.bprintf (PRINT_MEDIUM, "%s %s.\n", make_white(self->client->pers.netname), message);
337 if (deathmatch->value)
338 self->client->resp.score--;
339 self->enemy = NULL;
340 return;
341 }
342
343 self->enemy = attacker;
344 if (attacker && (attacker->client || attacker->svflags & SVF_MONSTER))
345 {
346 if ( (headShot) &&
347 (mod==MOD_BLASTER2 ||mod==MOD_SHOTGUN2 ||
348 mod==MOD_SSHOTGUN2 ||mod==MOD_PUNCH ||
349 mod==MOD_CHAINGUN2 ||mod==MOD_ROCKET2 ||
350 mod==MOD_BLASTER ||mod==MOD_SHOTGUN ||
351 mod==MOD_SSHOTGUN ||mod==MOD_MACHINEGUN ||
352 mod==MOD_CHAINGUN ||mod==MOD_ROCKET ||
353 mod==MOD_FIRE ||mod==MOD_HYPERBLASTER ||
354 mod==MOD_RAILGUN ||mod==MOD_GRAPPLE ||
355 mod==MOD_KICK ||mod==MOD_MAGIC ||
356 mod==MOD_SMACK ))
357 {
358 switch (mod)
359 {
360 case MOD_BLASTER:
361 message = "'s head was split open by";
362 message2 = "'s .50 Calibre Handgun";
363 break;
364 case MOD_SHOTGUN:
365 message = "was shot in the head by";
366 message2 = "'s AutoShotgun";
367 break;
368 case MOD_SSHOTGUN:
369 message = "was shot in the head by";
370 message2 = "'s 12 Gauge Shotgun";
371 break;
372 case MOD_MACHINEGUN:
373 message = "was shot in the head by";
374 message2 = "'s SubMachinegun";
375 break;
376 case MOD_CHAINGUN:
377 message = "'s head was mutilated by";
378 message2 = "'s Chaingun";
379 break;
380 case MOD_ROCKET:
381 message = "had their head split open courtesy of";
382 message2 = "'s Rocket";
383 break;
384 case MOD_BLASTER2:
385 message = "'s head was split open by";
386 message2 = "'s Laser Blaster";
387 break;
388 case MOD_SHOTGUN2:
389 message = "was shot in the head by";
390 message2 = "'s Autocannon";
391 break;
392 case MOD_SSHOTGUN2:
393 message = "was shot in the head by";
394 message2 = "'s .50 Calibre HandCannon";
395 break;
396 case MOD_CHAINGUN2:
397 message = "'s head was mutilated by";
398 message2 = "'s Heavy AutoShotgun";
399 break;
400 case MOD_ROCKET2:
401 message = "had their head split open courtesy of";
402 message2 = "'s Indirect Fire Rocket";
403 break;
404 case MOD_HYPERBLASTER:
405 message = "'s head was melted by";
406 message2 = "'s Flamethrower";
407 break;
408 case MOD_RAILGUN:
409 message = "took";
410 message2 =(IsNeutral(self))? "'s Uranium shell through its head":
411 (IsFemale(self))? "'s Uranium shell through her head":
412 "'s Uranium shell through his head";
413 break;
414 case MOD_GRAPPLE:
415 message = "'s head was torn open by";
416 message2 = "'s grappling hook";
417 break;
418 case MOD_FIRE:
419 message = "'s head was charred beyond recognition by";
420 message2 = "'s pyrotechnics";
421 break;
422 case MOD_KICK:
423 message = "'s head was smashed open by";
424 message2 = "'s foot";
425 break;
426 case MOD_PUNCH:
427 message = "'s head was smashed open by";
428 message2 = "'s iron fist";
429 break;
430 case MOD_MAGIC:
431 message = "'s head was ripped apart by";
432 message2 = (attacker->client)? "'s Magic(tm)" : "'s Turret";
433 break;
434 case MOD_SMACK:
435 message = "'s head smashed open by";
436 message2 = "'s Pistol";
437 break;
438 default:
439 break;
440 }
441 }
442 else
443 {
444 switch (mod)
445 {
446 case MOD_HIT:
447 message = "was beat to death by";
448 break;
449 case MOD_BLASTER:
450 message = "was shot down by";
451 message2 = "'s .50 caliber handgun";
452 break;
453 case MOD_SHOTGUN:
454 message = "was gunned down by";
455 break;
456 case MOD_SSHOTGUN:
457 message = "was filled with lead by";
458 message2 = "'s 12 Gauge shotgun";
459 break;
460 case MOD_MACHINEGUN:
461 message = "was machinegunned by";
462 break;
463 case MOD_CHAINGUN:
464 message = "was torn apart by";
465 message2 = "'s chaingun";
466 break;
467 case MOD_GRENADE:
468 message = "was popped by";
469 message2 = "'s grenade";
470 break;
471 case MOD_G_SPLASH:
472 message = "was shredded by";
473 message2 = "'s shrapnel";
474 break;
475 case MOD_ROCKET:
476 message = "ate";
477 message2 = "'s rocket";
478 break;
479 case MOD_BLASTER2:
480 message = "was shot down by";
481 message2 = "'s flare-cased Shell";
482 break;
483 case MOD_SHOTGUN2:
484 message = "was blown away by";
485 break;
486 case MOD_SSHOTGUN2:
487 message = "was filled with lead by";
488 message2 = "'s .50 Calibre HandCannon";
489 break;
490 case MOD_CHAINGUN2:
491 message = "was torn apart by";
492 message2 = "'s Heavy AutoShotgun";
493 break;
494 case MOD_ROCKET2:
495 message = "ate";
496 message2 = "'s Indirect-fire Rocket";
497 break;
498 case MOD_R_SPLASH:
499 message = "tried to dodge";
500 message2 = "'s rocket";
501 break;
502 case MOD_HYPERBLASTER:
503 message = "was torched by";
504 break;
505 case MOD_RAILGUN:
506 message = "took";
507 message2 =(IsNeutral(self))? "'s Uranium shell through its body":
508 (IsFemale(self))? "'s Uranium shell through her body":
509 "'s Uranium shell through his body";
510 break;
511 case MOD_BFG_LASER:
512 message = "was melted by";
513 message2 = "'s Heavy Laser";
514 break;
515 case MOD_BFG_BLAST:
516 message = "was disintegrated by";
517 message2 = "'s BFG blast";
518 break;
519 case MOD_BFG_EFFECT:
520 message = "couldn't hide from";
521 message2 = "'s BFG";
522 break;
523 case MOD_ROCKET_BFG:
524 message = "caught";
525 message2 = "'s BFG Rocket";
526 break;
527 case MOD_R_BFG_SPLASH:
528 message = "couldn't hide from";
529 message2 = "'s BFG Rocket";
530 break;
531 case MOD_HANDGRENADE:
532 message = "caught";
533 message2 = "'s handgrenade";
534 break;
535 case MOD_HG_SPLASH:
536 message = "didn't see";
537 message2 = "'s handgrenade";
538 break;
539 case MOD_GRAPPLE:
540 message = "was torn apart by";
541 message2 = "'s grappling hook";
542 break;
543 case MOD_C4:
544 message = "was liquidated by";
545 message2 = "'s slab of c4";
546 break;
547 case MOD_HELD_GRENADE:
548 message = "was grenaded by";
549 message2 = "'s grenade";
550 break;
551 case MOD_GREN_HIT:
552 message = "was smacked hard by";
553 message2 = "'s grenade";
554 break;
555 case MOD_FIRE:
556 message = "was incinerated by";
557 break;
558 case MOD_KICK:
559 message = " had a dose of";
560 message2 = "'s lethal roundhouse";
561 break;
562 case MOD_PUNCH:
563 message = " was beat down by";
564 break;
565 case MOD_MAGIC:
566 message = "was killed by";
567 message2 = (attacker->client)? "'s Magic(tm)" : "'s Turret";
568 break;
569 case MOD_GASGREN:
570 message = "was suffocated by";
571 message2 = "'s Mustard Gas";
572 break;
573 case MOD_SMACK:
574 message = "was beat down by";
575 message2 = "'s Pistol";
576 break;
577 case MOD_GAS_BOOM:
578 message = "went to hell courtesy of";
579 break;
580 case MOD_TAZER:
581 message = "was electrocuted by";
582 break;
583 case MOD_TELEFRAG:
584 message = "was telefragged by";
585 //message = "tried to invade";
586 //message2 = "'s personal space";
587 break;
588 default:
589 break;
590 }
591 }
592
593 if (message)
594 {
595 gi.bprintf (PRINT_MEDIUM,"%s %s %s%s\n", make_green(self->client->pers.netname), message,
596 make_white( (attacker->client)? attacker->client->pers.netname :
597 (!Q_stricmp (attacker->classname, "monster_berserk"))? "a Berserker" :
598 (!Q_stricmp (attacker->classname, "monster_gladiator"))? "a Gladiator" :
599 (!Q_stricmp (attacker->classname, "monster_gunner"))? "a Pyro Gunner" :
600 (!Q_stricmp (attacker->classname, "monster_infantry"))? "an Elite Infantryman" :
601 (!Q_stricmp (attacker->classname, "monster_soldier_light"))? "a Light Soldier" :
602 (!Q_stricmp (attacker->classname, "monster_soldier"))? "a Soldier" :
603 (!Q_stricmp (attacker->classname, "monster_soldier_ss"))? "an Elite Soldier" :
604 (!Q_stricmp (attacker->classname, "turret_driver"))? "a Gunner Infantryman" :
605 (!Q_stricmp (attacker->classname, "monster_medic"))? "a Medic" :
606 (!Q_stricmp (attacker->classname, "monster_chick"))? "a Strogg Cyber-Female" :
607 (!Q_stricmp (attacker->classname, "monster_brain"))? "a Guardian" :
608 (!Q_stricmp (attacker->classname, "monster_mutant"))? "a Mutant" :
609 (!Q_stricmp (attacker->classname, "monster_flyer"))? "a Sentinel" :
610 (!Q_stricmp (attacker->classname, "monster_floater"))? "a Brain" :
611 (!Q_stricmp (attacker->classname, "monster_flipper"))? "a Pirhanna" :
612 (!Q_stricmp (attacker->classname, "monster_hover"))? "an Air Gunner" :
613 (!Q_stricmp (attacker->classname, "monster_parasite"))? "a Parasite" :
614 (!Q_stricmp (attacker->classname, "monster_tank"))? "a Ground Tank" :
615 (!Q_stricmp (attacker->classname, "monster_tank_commander"))? "a Tank Commander" :
616 (!Q_stricmp (attacker->classname, "monster_supertank"))? "a Super Ground Tank" :
617 (!Q_stricmp (attacker->classname, "monster_boss2"))? "an Air Tank" :
618 (!Q_stricmp (attacker->classname, "monster_boss3_stand"))? "a Strogg Makron" :
619 (!Q_stricmp (attacker->classname, "monster_makron"))? "a Strogg Makron" :
620 (!Q_stricmp (attacker->classname, "monster_jorg"))? "a Strogg Jorg" :
621 "an enemy"), message2);
622
623 if (attacker->client)
624 {
625 if (ff)
626 attacker->client->resp.score--;
627 else
628 {
629 attacker->client->resp.score++;
630 if (headShot)
631 attacker->client->resp.headshots++;
632
633 }
634 }
635 return;
636 }
637 }
638 }
639 if (self)
640 if (self->client)
641 make_white(self->client->pers.netname);
642 if (attacker)
643 if (attacker->client)
644 make_white(attacker->client->pers.netname);
645
646 gi.bprintf (PRINT_MEDIUM,"%s died.\n", make_white(self->client->pers.netname));
647 if (deathmatch->value)
648 self->client->resp.score--;
649 }
650
651
652 void Touch_Item (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf);
653
TossClientWeapon(edict_t * self)654 void TossClientWeapon (edict_t *self)
655 {
656 gitem_t *item;
657 edict_t *drop;
658 qboolean quad;
659 float spread;
660
661 if (!deathmatch->value)
662 return;
663
664 item = self->client->pers.weapon;
665 if (! self->client->pers.inventory[self->client->ammo_index] )
666 item = NULL;
667 if (item && (strcmp (item->pickup_name, "Blaster") == 0))
668 item = NULL;
669
670 if (!((int)(dmflags->value) & DF_QUAD_DROP))
671 quad = false;
672 else
673 quad = (self->client->quad_framenum > (level.framenum + 10));
674
675 if (item && quad)
676 spread = 22.5;
677 else
678 spread = 0.0;
679
680 if (item)
681 {
682 self->client->v_angle[YAW] -= spread;
683 drop = Drop_Item (self, item);
684 self->client->v_angle[YAW] += spread;
685 drop->spawnflags = DROPPED_PLAYER_ITEM;
686 }
687
688 if (quad)
689 {
690 self->client->v_angle[YAW] += spread;
691 drop = Drop_Item (self, FindItemByClassname ("item_quad"));
692 self->client->v_angle[YAW] -= spread;
693 drop->spawnflags |= DROPPED_PLAYER_ITEM;
694
695 drop->touch = Touch_Item;
696 drop->nextthink = level.time + (self->client->quad_framenum - level.framenum) * FRAMETIME;
697 drop->think = G_FreeEdict;
698 }
699 }
700
701
702 /*
703 ==================
704 LookAtKiller
705 ==================
706 */
LookAtKiller(edict_t * self,edict_t * inflictor,edict_t * attacker)707 void LookAtKiller (edict_t *self, edict_t *inflictor, edict_t *attacker)
708 {
709 vec3_t dir;
710
711 if (attacker && attacker != world && attacker != self)
712 {
713 VectorSubtract (attacker->s.origin, self->s.origin, dir);
714 }
715 else if (inflictor && inflictor != world && inflictor != self)
716 {
717 VectorSubtract (inflictor->s.origin, self->s.origin, dir);
718 }
719 else
720 {
721 self->client->killer_yaw = self->s.angles[YAW];
722 return;
723 }
724
725 if (dir[0])
726 self->client->killer_yaw = 180/M_PI*atan2(dir[1], dir[0]);
727 else {
728 self->client->killer_yaw = 0;
729 if (dir[1] > 0)
730 self->client->killer_yaw = 90;
731 else if (dir[1] < 0)
732 self->client->killer_yaw = -90;
733 }
734 if (self->client->killer_yaw < 0)
735 self->client->killer_yaw += 360;
736
737
738 }
739
740 /*
741 ==================
742 player_die
743 ==================
744 */
745 #define MOD_GRENADE 6
746 #define MOD_G_SPLASH 7
747 #define MOD_ROCKET 8
748 #define MOD_R_SPLASH 9
749 #define MOD_BFG_LASER 12
750 #define MOD_BFG_BLAST 13
751 #define MOD_HANDGRENADE 15
752 #define MOD_HG_SPLASH 16
753 #define MOD_LAVA 19
754 #define MOD_EXPLOSIVE 25
755 #define MOD_BARREL 26
756 #define MOD_BOMB 27
757 #define MOD_C4 35
758 #define MOD_GREN_HIT 36
759 #define MOD_FIRE 37
760 #define MOD_ROCKET_BFG 39
761 #define MOD_R_BFG_SPLASH 40
762 #define MOD_MAGIC 41
763 #define MOD_GRENADE2 47
764 #define MOD_G_SPLASH2 48
765 #define MOD_ROCKET2 49
766 #define MOD_FLAME 55
767 #define MOD_GAS_BOOM 56
768 #define MOD_DISCHARGE 58
769
player_die(edict_t * self,edict_t * inflictor,edict_t * attacker,int damage,vec3_t point)770 void player_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
771 {
772 int mod = meansOfDeath & ~MOD_FRIENDLY_FIRE;
773 edict_t *ent, *newBody;
774 int n, j;
775
776 switch (mod)
777 {
778 case MOD_ROCKET:
779 case MOD_BFG_LASER:
780 case MOD_BFG_BLAST:
781 case MOD_HANDGRENADE:
782 case MOD_HELD_GRENADE:
783 case MOD_BOMB:
784 case MOD_C4:
785 case MOD_GAS_BOOM:
786 self->health = -500;
787 break;
788 case MOD_FLAME:
789 case MOD_DISCHARGE:
790 case MOD_LAVA:
791 self->health -= damage * 2;
792 break;
793 case MOD_HG_SPLASH:
794 case MOD_R_SPLASH:
795 case MOD_G_SPLASH:
796 self->health -= damage * 5;
797 break;
798 case MOD_R_BFG_SPLASH:
799 self->health -= damage * 10;
800 break;
801 }
802
803 VectorClear (self->avelocity);
804
805 self->takedamage = DAMAGE_YES;
806 self->movetype = MOVETYPE_TOSS;
807
808 self->s.modelindex2 = 0; // remove linked weapon model
809
810 self->client->flashlight_on = 0; //turn off flashlight
811 self->client->laser_on = 0; //turn off laser sight
812 self->client->aquasuit = 0; //take off aqua suit
813 self->client->goggles = 0; //take off goggles
814
815 self->timer = 0; //take off goggles
816
817 self->flaming = 0;
818
819 self->client->jets = 0;
820
821 //SET WEAP MODES
822 self->client->gren_set = 0;
823 self->client->rock_set = 0;
824 self->client->chan_set = 0;
825 self->client->shot_set = 0;
826 self->client->mach_set = 0;
827 self->client->blst_set = 0;
828 self->client->hypr_set = 0;
829 self->client->bfg_set = 0;
830 self->client->rail_set = 0;
831 //SET WEAP MODES
832
833 self->client->ps.rdflags &= ~RDF_IRGOGGLES;
834 self->client->climbing = 0;
835 self->flashbanged = 0;
836 self->client->isOnTurret = 0;
837 self->client->weaphold = 0; //flag for laser sight
838
839 self->s.angles[0] = 0;
840 self->s.angles[2] = 0;
841
842 self->s.sound = 0;
843 self->client->weapon_sound = 0;
844
845 self->maxs[2] = -8;
846
847 self->floater = 1;
848
849 // self->solid = SOLID_NOT;
850 self->svflags |= SVF_DEADMONSTER;
851
852 if (self->client->kami==666)
853 {
854 self->s.effects &= ~EF_TELEPORTER;
855 self->s.renderfx &= ~RF_SHELL_DOUBLE;
856 self->client->kami=0;
857 self->health = -555;
858 T_RadiusDamage(self, self, 2500, self, 400, MOD_C4);
859 if (sv_serversideonly->value)
860 T_FlashRadius(self, self, 2500, self, 400);
861
862 target_earthquake_think (self);
863 bigExplosion(self->s.origin, vec3_origin, 2);
864 if (sv_serversideonly->value)
865 ExplodeMark (self, self->s.origin, level.time + 5 + random()*2 );
866 }
867
868 if (!self->deadflag)
869 {
870 self->client->respawn_time = level.time + 1.0;
871 LookAtKiller (self, inflictor, attacker);
872 self->client->ps.pmove.pm_type = PM_DEAD;
873 ClientObituary (self, inflictor, attacker);
874 TossClientWeapon (self);
875 if (deathmatch->value)
876 Cmd_Help_f (self); // show scores
877
878 // clear inventory
879 // this is kind of ugly, but it's how we want to handle keys in coop
880 for (n = 0; n < game.num_items; n++)
881 {
882 if (coop->value && itemlist[n].flags & IT_KEY)
883 self->client->resp.coop_respawn.inventory[n] = self->client->pers.inventory[n];
884 self->client->pers.inventory[n] = 0;
885 }
886 }
887
888 //remove linked ents
889 for (n= 0; n < self->linked_ents_num; n++)
890 {
891 if (self->linked_ents[n])
892 {
893 self->linked_ents[n]->movetype = MOVETYPE_TOSS;
894 self->linked_ents[n]->linkedto=NULL;
895 VectorClear(self->linked_ents[n]->velocity);
896 }
897 }
898 self->linked_ents_num = 0;
899
900 // remove powerups
901 self->client->quad_framenum = 0;
902 self->client->invincible_framenum = 0;
903 self->client->breather_framenum = 0;
904 self->client->enviro_framenum = 0;
905 self->flags &= ~FL_POWER_ARMOR;
906
907 if (self->health <= -500)
908 { // gib
909 gi.sound (self, CHAN_BODY, gi.soundindex ("misc/udeath.wav"), 1, ATTN_NORM, 0);
910 for (n= 0; n < 2; n++)
911 ThrowGib (self, "models/objects/gibs/bone/tris.md2", damage, GIB_ORGANIC);
912 for (n= 0; n < 4; n++)
913 ThrowGib (self, "models/objects/gibs/sm_meat/tris.md2", damage, GIB_ORGANIC);
914 ThrowGib (self, "models/objects/gibs/chest/tris.md2", damage, GIB_ORGANIC);
915 //ThrowClientHead (self, damage);
916 newBody = ThrowClientHeadNew (self);
917
918 self->takedamage = DAMAGE_NO;
919 }
920 else
921 { // normal death
922 if (!self->deadflag)
923 {
924 static int i;
925
926 i = (i+1)%3;
927 // start a death animation
928 self->client->anim_priority = ANIM_DEATH;
929 if (self->client->ps.pmove.pm_flags & PMF_DUCKED)
930 {
931 self->s.frame = FRAME_crdeath1;
932 self->client->anim_end = FRAME_crdeath5;
933 }
934 else switch (i)
935 {
936 case 0:
937 self->s.frame = FRAME_death101;
938 self->client->anim_end = FRAME_death106;
939 break;
940 case 1:
941 self->s.frame = FRAME_death201;
942 self->client->anim_end = FRAME_death206;
943 break;
944 case 2:
945 self->s.frame = FRAME_death301;
946 self->client->anim_end = FRAME_death308;
947 break;
948 }
949 gi.sound (self, CHAN_VOICE, gi.soundindex(va("*death%i.wav", (rand()%4)+1)), 1, ATTN_NORM, 0);
950
951
952 }
953 newBody=MakeNewBody (self);
954 }
955
956 self->deadflag = DEAD_DEAD;
957
958 if (!deathmatch->value||self->viewcam_on ||true)
959 {
960 if (attacker!=self&&(attacker->client || (attacker->svflags & SVF_MONSTER))&&self->linked_flame!=attacker)
961 {
962 self->killer = attacker;
963 self->solid = SOLID_NOT;
964 self->movetype = MOVETYPE_NOCLIP;
965 }
966 else
967 {
968 self->killer = newBody;
969 self->solid = SOLID_NOT;
970 self->movetype = MOVETYPE_NOCLIP;
971 }
972 self->viewcam_on = 2;
973 }
974
975 if(self->linked_flame)
976 {
977 self->linked_flame->flamed = newBody;
978 self->linked_flame=NULL;
979 }
980
981 for (j = 1; j <= game.maxentities; j++)
982 {
983 ent = &g_edicts[j];
984
985 if (ent->linkedto==self)
986 {
987 ent->linkedto = NULL;
988 ent->movetype = MOVETYPE_TOSS;
989 }
990 if (newBody)
991 {
992 if (ent->killer==self)
993 ent->killer=newBody;
994 if (ent->enemy==self)
995 ent->enemy=newBody;
996 }
997 }
998
999 self->client->viewcam_dist = 0;
1000 CleanUpEnt (self);
1001
1002 gi.linkentity (self);
1003
1004 }
1005
1006 //=======================================================================
1007
1008 /*
1009 ==============
1010 InitClientPersistant
1011
1012 This is only called when the game first initializes in single player,
1013 but is called after each death and level change in deathmatch
1014 ==============
1015 */
InitClientPersistant(gclient_t * client)1016 void InitClientPersistant (gclient_t *client)
1017 {
1018 gitem_t *item;
1019 int i;
1020
1021 memset (&client->pers, 0, sizeof(client->pers));
1022
1023 //*
1024 //***My Section Start***
1025 //*
1026 for (i=0;i>50+(random()*20)+(random()*20);i++)
1027 {
1028 if (random()>.9)
1029 i--;
1030 }
1031
1032 if (sv_waterlevel->value)
1033 {
1034 item = FindItem("Flashlight");
1035 client->pers.inventory[ITEM_INDEX(item)] = 1;
1036 item = FindItem("Laser Sight");
1037 client->pers.inventory[ITEM_INDEX(item)] = 1;
1038 }
1039 else if (deathmatch->value)
1040 {
1041
1042 item = FindItem("Flashlight");
1043 client->pers.inventory[ITEM_INDEX(item)] = 1;
1044
1045 if (sv_grapple->value)
1046 {
1047 item = FindItem("Grapple");
1048 client->pers.inventory[ITEM_INDEX(item)] = 1;
1049 }
1050
1051 if (random()<0.2 && (int)sv_banned_weapons->value!=-1)
1052 {
1053 item = FindItem("Laser Sight");
1054 client->pers.inventory[ITEM_INDEX(item)] = 1;
1055 }
1056 else if (crandom()<0.2)
1057 {
1058 item = FindItem("Diving Mask");
1059 client->pers.inventory[ITEM_INDEX(item)] = 1;
1060 }
1061 else if (crandom()<0.2)
1062 {
1063 item = FindItem("Jet Propulsion Unit");
1064 client->pers.inventory[ITEM_INDEX(item)] = 1;
1065 }
1066 else if ((int)sv_banned_weapons->value!=-1)
1067 {
1068 item = FindItem("Regen-Stealth Suit");
1069 client->pers.inventory[ITEM_INDEX(item)] = 1;
1070 }
1071 else
1072 {
1073 item = FindItem("Jet Propulsion Unit");
1074 client->pers.inventory[ITEM_INDEX(item)] = 1;
1075 }
1076
1077 }
1078 else if (coop->value)
1079 {
1080 if (sv_grapple->value)
1081 {
1082 item = FindItem("Grapple");
1083 client->pers.inventory[ITEM_INDEX(item)] = 1;
1084 }
1085 }
1086 //***My Section End***/
1087
1088 if ((int)sv_banned_weapons->value!=-1)
1089 {
1090 int banned = sv_banned_weapons->value;
1091
1092 if (!(banned&NO_BLASTER))
1093 {
1094 item = FindItem("Bullets");
1095 client->pers.inventory[ITEM_INDEX(item)] = 100;
1096 item = FindItem("Blaster");
1097 client->pers.selected_item = ITEM_INDEX(item);
1098 client->pers.inventory[client->pers.selected_item] = 1;
1099 }
1100 else if (!(banned&NO_SHOTGUN))
1101 {
1102 item = FindItem("Shells");
1103 client->pers.inventory[ITEM_INDEX(item)] = 100;
1104 item = FindItem("Shotgun");
1105 client->pers.selected_item = ITEM_INDEX(item);
1106 client->pers.inventory[client->pers.selected_item] = 1;
1107 }
1108 else if (!(banned&NO_SSHOTGUN))
1109 {
1110 item = FindItem("Shells");
1111 client->pers.inventory[ITEM_INDEX(item)] = 100;
1112 item = FindItem("Super Shotgun");
1113 client->pers.selected_item = ITEM_INDEX(item);
1114 client->pers.inventory[client->pers.selected_item] = 1;
1115 }
1116 else if (!(banned&NO_MACHINEGUN))
1117 {
1118 item = FindItem("Bullets");
1119 client->pers.inventory[ITEM_INDEX(item)] = 100;
1120 item = FindItem("Mashinegun");
1121 client->pers.selected_item = ITEM_INDEX(item);
1122 client->pers.inventory[client->pers.selected_item] = 1;
1123 }
1124 else if (!(banned&NO_CHAINGUN))
1125 {
1126 item = FindItem("Shells");
1127 client->pers.inventory[ITEM_INDEX(item)] = 100;
1128 item = FindItem("Chaingun");
1129 client->pers.selected_item = ITEM_INDEX(item);
1130 client->pers.inventory[client->pers.selected_item] = 1;
1131 }
1132 else if (!(banned&NO_GRENADE_LAUNCHER))
1133 {
1134 item = FindItem("grenades");
1135 client->pers.inventory[ITEM_INDEX(item)] = 20;
1136 item = FindItem("Grenade Launcher");
1137 client->pers.selected_item = ITEM_INDEX(item);
1138 client->pers.inventory[client->pers.selected_item] = 1;
1139 }
1140 else if (!(banned&NO_ROCKET_LAUNCHER))
1141 {
1142 item = FindItem("rockets");
1143 client->pers.inventory[ITEM_INDEX(item)] = 15;
1144 item = FindItem("Rocket Launcher");
1145 client->pers.selected_item = ITEM_INDEX(item);
1146 client->pers.inventory[client->pers.selected_item] = 1;
1147 }
1148 else if (!(banned&NO_HYPERBLASTER))
1149 {
1150 item = FindItem("Cells");
1151 client->pers.inventory[ITEM_INDEX(item)] = 100;
1152 item = FindItem("HyperBlaster");
1153 client->pers.selected_item = ITEM_INDEX(item);
1154 client->pers.inventory[client->pers.selected_item] = 1;
1155 }
1156 else if (!(banned&NO_RAILGUN) && !(banned&NO_TAZER))
1157 {
1158 if (!(banned&NO_RAILGUN))
1159 {
1160 item = FindItem("Slugs");
1161 client->pers.inventory[ITEM_INDEX(item)] = 10;
1162 }
1163 if (!(banned&NO_TAZER))
1164 {
1165 item = FindItem("Cells");
1166 client->pers.inventory[ITEM_INDEX(item)] = 75;
1167 }
1168 item = FindItem("Railgun");
1169 client->pers.selected_item = ITEM_INDEX(item);
1170 client->pers.inventory[client->pers.selected_item] = 1;
1171 }
1172 else if (!(banned&NO_ROCKETBFG) && !(banned&NO_BFGLASER))
1173 {
1174 if (!(banned&NO_ROCKETBFG))
1175 {
1176 item = FindItem("Rockets");
1177 client->pers.inventory[ITEM_INDEX(item)] = 100;
1178 }
1179 if (!(banned&NO_BFGLASER))
1180 {
1181 item = FindItem("Cells");
1182 client->pers.inventory[ITEM_INDEX(item)] = 100;
1183 }
1184 item = FindItem("BFG10K");
1185 client->pers.selected_item = ITEM_INDEX(item);
1186 client->pers.inventory[client->pers.selected_item] = 1;
1187 }
1188 else if (!(banned&NO_GRENADES))
1189 {
1190 item = FindItem("Grenades");
1191 client->pers.selected_item = ITEM_INDEX(item);
1192 client->pers.inventory[client->pers.selected_item] = 10;
1193 }
1194 base_weapon = item->pickup_name;
1195 }
1196
1197 client->pers.weapon = item;
1198
1199 client->pers.health = 100;
1200 client->pers.max_health = 100;
1201
1202 client->pers.max_bullets = 200;
1203 client->pers.max_shells = 200;
1204 client->pers.max_rockets = 100;
1205 client->pers.max_grenades = 25;
1206 client->pers.max_cells = 100;
1207 client->pers.max_slugs = 10;
1208
1209 client->pers.connected = true;
1210 }
1211
1212
InitClientResp(gclient_t * client)1213 void InitClientResp (gclient_t *client)
1214 {
1215 memset (&client->resp, 0, sizeof(client->resp));
1216 client->resp.enterframe = level.framenum;
1217 client->resp.coop_respawn = client->pers;
1218 }
1219
1220 /*
1221 ==================
1222 SaveClientData
1223
1224 Some information that should be persistant, like health,
1225 is still stored in the edict structure, so it needs to
1226 be mirrored out to the client structure before all the
1227 edicts are wiped.
1228 ==================
1229 */
SaveClientData(void)1230 void SaveClientData (void)
1231 {
1232 int i;
1233 edict_t *ent;
1234
1235 for (i=0 ; i<game.maxclients ; i++)
1236 {
1237 ent = &g_edicts[1+i];
1238 if (!ent->inuse)
1239 continue;
1240
1241 game.clients[i].pers.chasetoggle = ent->client->chasetoggle;
1242
1243 game.clients[i].pers.health = ent->health;
1244 game.clients[i].pers.max_health = ent->max_health;
1245 game.clients[i].pers.savedFlags = (ent->flags & (FL_GODMODE|FL_NOTARGET|FL_POWER_ARMOR));
1246
1247 game.clients[i].pers.grappleType = ent->grappleType;
1248 game.clients[i].pers.bfg_laser_type = ent->bfg_laser_type;
1249 game.clients[i].pers.Player_ID = ent->Player_ID;
1250 game.clients[i].pers.TeamName = ent->TeamName;
1251 game.clients[i].pers.viewcam_on = ent->viewcam_on;
1252
1253 if (coop->value)
1254 {
1255 game.clients[i].pers.score = ent->client->resp.score;
1256 game.clients[i].pers.headshots = ent->client->resp.headshots;
1257 }
1258 }
1259 }
1260
FetchClientEntData(edict_t * ent)1261 void FetchClientEntData (edict_t *ent)
1262 {
1263 ent->health = ent->client->pers.health;
1264 ent->max_health = ent->client->pers.max_health;
1265 ent->flags |= ent->client->pers.savedFlags;
1266
1267 if (!deathmatch->value&&!coop->value)
1268 {
1269 ent->grappleType = ent->client->pers.grappleType;
1270 ent->bfg_laser_type = ent->client->pers.bfg_laser_type;
1271 ent->Player_ID = ent->client->pers.Player_ID;
1272 ent->TeamName = ent->client->pers.TeamName;
1273 ent->viewcam_on = ent->client->pers.viewcam_on;
1274 }
1275
1276 if (coop->value)
1277 {
1278 ent->client->resp.score = ent->client->pers.score;
1279 ent->client->resp.headshots = ent->client->pers.headshots;
1280 }
1281 }
1282
1283
1284
1285 /*
1286 =======================================================================
1287
1288 SelectSpawnPoint
1289
1290 =======================================================================
1291 */
1292
1293 /*
1294 ================
1295 PlayersRangeFromSpot
1296
1297 Returns the distance to the nearest player from the given spot
1298 ================
1299 */
PlayersRangeFromSpot(edict_t * spot)1300 float PlayersRangeFromSpot (edict_t *spot)
1301 {
1302 edict_t *player;
1303 float bestplayerdistance;
1304 vec3_t v;
1305 int n;
1306 float playerdistance;
1307
1308
1309 bestplayerdistance = 9999999;
1310
1311 for (n = 1; n <= maxclients->value; n++)
1312 {
1313 player = &g_edicts[n];
1314
1315 if (!player->inuse)
1316 continue;
1317
1318 if (player->health <= 0)
1319 continue;
1320
1321 VectorSubtract (spot->s.origin, player->s.origin, v);
1322 playerdistance = VectorLength (v);
1323
1324 if (playerdistance < bestplayerdistance)
1325 bestplayerdistance = playerdistance;
1326 }
1327
1328 return bestplayerdistance;
1329 }
1330
1331 /*
1332 ================
1333 SelectRandomDeathmatchSpawnPoint
1334
1335 go to a random point, but NOT the two points closest
1336 to other players
1337 ================
1338 */
SelectRandomDeathmatchSpawnPoint(void)1339 edict_t *SelectRandomDeathmatchSpawnPoint (void)
1340 {
1341 edict_t *spot, *spot1, *spot2;
1342 int count = 0;
1343 int selection;
1344 float range, range1, range2;
1345
1346 spot = NULL;
1347 range1 = range2 = 99999;
1348 spot1 = spot2 = NULL;
1349
1350 while ((spot = G_Find (spot, FOFS(classname), "info_player_deathmatch")) != NULL)
1351 {
1352 count++;
1353 range = PlayersRangeFromSpot(spot);
1354 if (range < range1)
1355 {
1356 range1 = range;
1357 spot1 = spot;
1358 }
1359 else if (range < range2)
1360 {
1361 range2 = range;
1362 spot2 = spot;
1363 }
1364 }
1365
1366 if (!count)
1367 return NULL;
1368
1369 if (count <= 2)
1370 {
1371 spot1 = spot2 = NULL;
1372 }
1373 else
1374 count -= 2;
1375
1376 selection = rand() % count;
1377
1378 spot = NULL;
1379 do
1380 {
1381 spot = G_Find (spot, FOFS(classname), "info_player_deathmatch");
1382 if (spot == spot1 || spot == spot2)
1383 selection++;
1384 } while(selection--);
1385
1386 return spot;
1387 }
1388
1389 /*
1390 ================
1391 SelectFarthestDeathmatchSpawnPoint
1392
1393 ================
1394 */
SelectFarthestDeathmatchSpawnPoint(void)1395 edict_t *SelectFarthestDeathmatchSpawnPoint (void)
1396 {
1397 edict_t *bestspot;
1398 float bestdistance, bestplayerdistance;
1399 edict_t *spot;
1400
1401
1402 spot = NULL;
1403 bestspot = NULL;
1404 bestdistance = 0;
1405 while ((spot = G_Find (spot, FOFS(classname), "info_player_deathmatch")) != NULL)
1406 {
1407 bestplayerdistance = PlayersRangeFromSpot (spot);
1408
1409 if (bestplayerdistance > bestdistance)
1410 {
1411 bestspot = spot;
1412 bestdistance = bestplayerdistance;
1413 }
1414 }
1415
1416 if (bestspot)
1417 {
1418 return bestspot;
1419 }
1420
1421 // if there is a player just spawned on each and every start spot
1422 // we have no choice to turn one into a telefrag meltdown
1423 spot = G_Find (NULL, FOFS(classname), "info_player_deathmatch");
1424
1425 return spot;
1426 }
1427
SelectDeathmatchSpawnPoint(void)1428 edict_t *SelectDeathmatchSpawnPoint (void)
1429 {
1430 if ( (int)(dmflags->value) & DF_SPAWN_FARTHEST)
1431 return SelectFarthestDeathmatchSpawnPoint ();
1432 else
1433 return SelectRandomDeathmatchSpawnPoint ();
1434 }
1435
1436
SelectCoopSpawnPoint(edict_t * ent)1437 edict_t *SelectCoopSpawnPoint (edict_t *ent)
1438 {
1439 int index;
1440 edict_t *spot = NULL;
1441 char *target;
1442
1443 index = ent->client - game.clients;
1444
1445 // player 0 starts in normal player spawn point
1446 if (!index)
1447 return NULL;
1448
1449 spot = NULL;
1450
1451 // assume there are four coop spots at each spawnpoint
1452 while (1)
1453 {
1454 spot = G_Find (spot, FOFS(classname), "info_player_coop");
1455 if (!spot)
1456 return NULL; // we didn't have enough...
1457
1458 target = spot->targetname;
1459 if (!target)
1460 target = "";
1461 if ( Q_stricmp(game.spawnpoint, target) == 0 )
1462 { // this is a coop spawn point for one of the clients here
1463 index--;
1464 if (!index)
1465 return spot; // this is it
1466 }
1467 }
1468
1469
1470 return spot;
1471 }
1472
1473
1474 /*
1475 ===========
1476 SelectSpawnPoint
1477
1478 Chooses a player start, deathmatch start, coop start, etc
1479 ============
1480 */
SelectSpawnPoint(edict_t * ent,vec3_t origin,vec3_t angles)1481 void SelectSpawnPoint (edict_t *ent, vec3_t origin, vec3_t angles)
1482 {
1483 edict_t *spot = NULL;
1484
1485 if (deathmatch->value)
1486 spot = SelectDeathmatchSpawnPoint ();
1487 else if (coop->value)
1488 spot = SelectCoopSpawnPoint (ent);
1489
1490 // find a single player start spot
1491 if (!spot)
1492 {
1493 while ((spot = G_Find (spot, FOFS(classname), "info_player_start")) != NULL)
1494 {
1495 if (!game.spawnpoint[0] && !spot->targetname)
1496 break;
1497
1498 if (!game.spawnpoint[0] || !spot->targetname)
1499 continue;
1500
1501 if (Q_stricmp(game.spawnpoint, spot->targetname) == 0)
1502 break;
1503 }
1504
1505 if (!spot)
1506 {
1507 if (!game.spawnpoint[0])
1508 { // there wasn't a spawnpoint without a target, so use any
1509 spot = G_Find (spot, FOFS(classname), "info_player_start");
1510 }
1511 if (!spot)
1512 gi.error ("Couldn't find spawn point %s\n", game.spawnpoint);
1513 }
1514 }
1515
1516 VectorCopy (spot->s.origin, origin);
1517 origin[2] += 9;
1518 VectorCopy (spot->s.angles, angles);
1519 }
1520
1521 //======================================================================
1522
1523
InitBodyQue(void)1524 void InitBodyQue (void)
1525 {
1526 int i;
1527 edict_t *ent;
1528
1529 level.body_que = 0;
1530 for (i=0; i<BODY_QUEUE_SIZE ; i++)
1531 {
1532 ent = G_Spawn();
1533 ent->classname = "bodyque";
1534 }
1535 }
1536
body_die(edict_t * self,edict_t * inflictor,edict_t * attacker,int damage,vec3_t point)1537 void body_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
1538 {
1539 int n;
1540 int dmg = 0;
1541 if (self->health < -500)
1542 {
1543 gi.sound (self, CHAN_BODY, gi.soundindex ("misc/udeath.wav"), 1, ATTN_NORM, 0);
1544
1545 for (n= 0; n < 2; n++)
1546 ThrowGib (self, "models/objects/gibs/bone/tris.md2", dmg, GIB_ORGANIC);
1547 for (n= 0; n < 4; n++)
1548 ThrowGib (self, "models/objects/gibs/sm_meat/tris.md2", dmg, GIB_ORGANIC);
1549 ThrowGib (self, "models/objects/gibs/chest/tris.md2", dmg, GIB_ORGANIC);
1550 self->takedamage = DAMAGE_NO;
1551 ThrowClientHeadNew (self);
1552 G_FreeEdict(self);
1553 return;
1554 }
1555
1556 }
1557
body_touch(edict_t * ent,edict_t * other,cplane_t * plane,csurface_t * surf)1558 static void body_touch (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf)
1559 {
1560 if (!other->takedamage)
1561 {
1562 if (!(surf && (surf->flags & SURF_SKY)))
1563 gi.sound (ent, CHAN_VOICE, gi.soundindex ("world/land.wav"), 0.5, ATTN_NORM, 0);
1564 }
1565 }
1566
body_think(edict_t * ent)1567 void body_think (edict_t *ent)
1568 {
1569 if (ent->s.frame==FRAME_crdeath5 || ent->s.frame==FRAME_death106 ||
1570 ent->s.frame==FRAME_death206 || ent->s.frame==FRAME_death308)
1571 {
1572 ent->think=FadeDieSink;
1573 ent->nextthink=level.time+FADETIME+random()*5;
1574 return;
1575 }
1576
1577 if (ent->s.frame>=135 && ent->s.frame<=172)
1578 ent->s.frame=FRAME_crdeath1;
1579 else if (ent->s.frame<135)
1580 {
1581 if ((int)(crandom()*3)==1)
1582 ent->s.frame=FRAME_death101;
1583 else if ((int)(crandom()*3)==1)
1584 ent->s.frame=FRAME_death201;
1585 else
1586 ent->s.frame=FRAME_death301;
1587 }
1588 else if ((ent->s.frame>=FRAME_crdeath1 && ent->s.frame<=FRAME_crdeath4)||
1589 (ent->s.frame>=FRAME_death101 && ent->s.frame<=FRAME_death105)||
1590 (ent->s.frame>=FRAME_death201 && ent->s.frame<=FRAME_death205)||
1591 (ent->s.frame>=FRAME_death301 && ent->s.frame<=FRAME_death307))
1592
1593 {
1594 ent->s.frame++;
1595 }
1596
1597 ent->nextthink=level.time+FRAMETIME;
1598 }
1599
MakeNewBody(edict_t * ent)1600 edict_t * MakeNewBody (edict_t *ent)
1601 {
1602 edict_t *body;
1603
1604 body=G_Spawn();
1605
1606 body->s = ent->s;
1607 body->s.number = body - g_edicts;
1608 VectorCopy (ent->mins, body->mins);
1609 VectorCopy (ent->maxs, body->maxs);
1610 VectorCopy (ent->absmin, body->absmin);
1611 VectorCopy (ent->absmax, body->absmax);
1612 VectorCopy (ent->size, body->size);
1613 VectorCopy (ent->velocity, body->velocity);
1614 body->solid = ent->solid;
1615 body->clipmask = ent->clipmask;
1616 body->owner = ent->owner;
1617 body->movetype = ent->movetype;
1618
1619 body->svflags = SVF_DEADMONSTER;
1620
1621 body->die = body_die;
1622 body->takedamage = DAMAGE_YES;
1623 body->movetype = MOVETYPE_TOSS;
1624
1625 body->touch = body_touch;
1626
1627 body->think=body_think;
1628 body->nextthink=level.time+FRAMETIME;
1629 body->floater=1;
1630
1631 body->s.renderfx |= RF_IR_VISIBLE;
1632
1633 if (ent->client)
1634 body->PlayerDeadName = ent->client->pers.netname;
1635
1636 gi.linkentity (body);
1637
1638 return body;
1639 }
1640
CopyToBodyQue(edict_t * ent)1641 void CopyToBodyQue (edict_t *ent)
1642 {
1643 edict_t *body;
1644
1645 // grab a body que and cycle to the next one
1646 body = &g_edicts[(int)maxclients->value + level.body_que + 1];
1647 level.body_que = (level.body_que + 1) % BODY_QUEUE_SIZE;
1648
1649 // FIXME: send an effect on the removed body
1650
1651 gi.unlinkentity (ent);
1652
1653 gi.unlinkentity (body);
1654 body->s = ent->s;
1655 body->s.number = body - g_edicts;
1656
1657 body->svflags = ent->svflags;
1658 VectorCopy (ent->mins, body->mins);
1659 VectorCopy (ent->maxs, body->maxs);
1660 VectorCopy (ent->absmin, body->absmin);
1661 VectorCopy (ent->absmax, body->absmax);
1662 VectorCopy (ent->size, body->size);
1663 body->solid = ent->solid;
1664 body->clipmask = ent->clipmask;
1665 body->owner = ent->owner;
1666 body->movetype = ent->movetype;
1667
1668 body->die = body_die;
1669 body->takedamage = DAMAGE_YES;
1670
1671 body->think=FadeDieSink;
1672 body->nextthink=level.time+FADETIME+random()*5;
1673 body->floater=1;
1674
1675 body->s.renderfx |= RF_IR_VISIBLE;
1676
1677 gi.linkentity (body);
1678 }
1679
1680
respawn(edict_t * self)1681 void respawn (edict_t *self)
1682 {
1683 if (deathmatch->value || coop->value)
1684 {
1685 // spectator's don't leave bodies
1686 // if (self->movetype != MOVETYPE_NOCLIP)
1687 // CopyToBodyQue (self);
1688 self->svflags &= ~SVF_NOCLIENT;
1689 PutClientInServer (self);
1690
1691 // add a teleportation effect
1692 self->s.event = EV_PLAYER_TELEPORT;
1693
1694 // hold in place briefly
1695 self->client->ps.pmove.pm_flags = PMF_TIME_TELEPORT;
1696 self->client->ps.pmove.pm_time = 1.1;
1697
1698 self->client->respawn_time = level.time;
1699
1700 return;
1701 }
1702
1703 // restart the entire server
1704 gi.AddCommandString ("menu_loadgame\n");
1705 }
1706
1707 /*
1708 * only called when pers.spectator changes
1709 * note that resp.spectator should be the opposite of pers.spectator here
1710 */
spectator_respawn(edict_t * ent)1711 void spectator_respawn (edict_t *ent)
1712 {
1713 int i, numspec;
1714
1715 // if the user wants to become a spectator, make sure he doesn't
1716 // exceed max_spectators
1717
1718 if (ent->client->pers.spectator) {
1719 char *value = Info_ValueForKey (ent->client->pers.userinfo, "spectator");
1720 if (*spectator_password->string &&
1721 strcmp(spectator_password->string, "none") &&
1722 strcmp(spectator_password->string, value)) {
1723 gi.cprintf(ent, PRINT_HIGH, "Spectator password incorrect.\n");
1724 ent->client->pers.spectator = false;
1725 gi.WriteByte (svc_stufftext);
1726 gi.WriteString ("spectator 0\n");
1727 gi.unicast(ent, true);
1728 return;
1729 }
1730
1731 // count spectators
1732 for (i = 1, numspec = 0; i <= maxclients->value; i++)
1733 if (g_edicts[i].inuse && g_edicts[i].client->pers.spectator)
1734 numspec++;
1735
1736 if (numspec >= maxspectators->value) {
1737 gi.cprintf(ent, PRINT_HIGH, "Server spectator limit is full.");
1738 ent->client->pers.spectator = false;
1739 // reset his spectator var
1740 gi.WriteByte (svc_stufftext);
1741 gi.WriteString ("spectator 0\n");
1742 gi.unicast(ent, true);
1743 return;
1744 }
1745 } else {
1746 // he was a spectator and wants to join the game
1747 // he must have the right password
1748 char *value = Info_ValueForKey (ent->client->pers.userinfo, "password");
1749 if (*password->string && strcmp(password->string, "none") &&
1750 strcmp(password->string, value)) {
1751 gi.cprintf(ent, PRINT_HIGH, "Password incorrect.\n");
1752 ent->client->pers.spectator = true;
1753 gi.WriteByte (svc_stufftext);
1754 gi.WriteString ("spectator 1\n");
1755 gi.unicast(ent, true);
1756 return;
1757 }
1758 }
1759
1760 // clear client on respawn
1761 ent->client->resp.score = ent->client->pers.score = 0;
1762 ent->client->resp.headshots = ent->client->pers.headshots = 0;
1763
1764 ent->svflags &= ~SVF_NOCLIENT;
1765 PutClientInServer (ent);
1766
1767 // add a teleportation effect
1768 if (!ent->client->pers.spectator) {
1769 // send effect
1770 gi.WriteByte (svc_muzzleflash);
1771 gi.WriteShort (ent-g_edicts);
1772 gi.WriteByte (MZ_LOGIN);
1773 gi.multicast (ent->s.origin, MULTICAST_PVS);
1774
1775 // hold in place briefly
1776 ent->client->ps.pmove.pm_flags = PMF_TIME_TELEPORT;
1777 ent->client->ps.pmove.pm_time = 14;
1778 }
1779
1780 ent->client->respawn_time = level.time;
1781
1782 if (ent->client->pers.spectator)
1783 gi.bprintf (PRINT_HIGH, "%s has moved to the sidelines\n", ent->client->pers.netname);
1784 else
1785 gi.bprintf (PRINT_HIGH, "%s joined the game\n", ent->client->pers.netname);
1786 }
1787
1788 //==============================================================
1789
1790
1791 /*
1792 ===========
1793 PutClientInServer
1794
1795 Called when a player connects to a server or respawns in
1796 a deathmatch.
1797 ============
1798 */
PutClientInServer(edict_t * ent)1799 void PutClientInServer (edict_t *ent)
1800 {
1801 vec3_t mins = {-16, -16, -24};
1802 vec3_t maxs = {16, 16, 32};
1803 int index;
1804 vec3_t spawn_origin, spawn_angles;
1805 gclient_t *client;
1806 int i;
1807 client_persistant_t saved;
1808 client_respawn_t resp;
1809 gitem_t *item;
1810
1811 int chasetoggle;
1812 char userinfo[MAX_INFO_STRING];
1813
1814 // find a spawn point
1815 // do it before setting health back up, so farthest
1816 // ranging doesn't count this client
1817 SelectSpawnPoint (ent, spawn_origin, spawn_angles);
1818
1819 index = ent-g_edicts-1;
1820 client = ent->client;
1821
1822 chasetoggle = client->pers.chasetoggle;
1823
1824 // deathmatch wipes most client data every spawn
1825 if (deathmatch->value)
1826 {
1827 char userinfo[MAX_INFO_STRING];
1828
1829 resp = client->resp;
1830 memcpy (userinfo, client->pers.userinfo, sizeof(userinfo));
1831 InitClientPersistant (client);
1832 ClientUserinfoChanged (ent, userinfo);
1833 }
1834 else if (coop->value)
1835 {
1836 // int n;
1837 char userinfo[MAX_INFO_STRING];
1838
1839 resp = client->resp;
1840 memcpy (userinfo, client->pers.userinfo, sizeof(userinfo));
1841 // this is kind of ugly, but it's how we want to handle keys in coop
1842 // for (n = 0; n < game.num_items; n++)
1843 // {
1844 // if (itemlist[n].flags & IT_KEY)
1845 // resp.coop_respawn.inventory[n] = client->pers.inventory[n];
1846 // }
1847 resp.coop_respawn.game_helpchanged = client->pers.game_helpchanged;
1848 resp.coop_respawn.helpchanged = client->pers.helpchanged;
1849 client->pers = resp.coop_respawn;
1850 ClientUserinfoChanged (ent, userinfo);
1851 if (resp.score > client->pers.score)
1852 client->pers.score = resp.score;
1853 if (resp.headshots > client->pers.headshots)
1854 client->pers.headshots = resp.headshots;
1855 }
1856 else
1857 {
1858 memset (&resp, 0, sizeof(resp));
1859
1860 memcpy (userinfo, client->pers.userinfo, sizeof(userinfo));
1861 ClientUserinfoChanged (ent, userinfo);
1862 }
1863
1864 // clear everything but the persistant data
1865 saved = client->pers;
1866 memset (client, 0, sizeof(*client));
1867 client->pers = saved;
1868 if (client->pers.health <= 0)
1869 InitClientPersistant(client);
1870 client->resp = resp;
1871
1872 client->pers.chasetoggle = chasetoggle;
1873
1874 if (coop->value)
1875 {
1876 client->pers.health = 100;
1877 ent->health = 100;
1878 }
1879
1880 // copy some data from the client to the entity
1881 FetchClientEntData (ent);
1882
1883 ent->linked_flame=NULL;
1884
1885 // clear entity values
1886 ent->groundentity = NULL;
1887 ent->client = &game.clients[index];
1888 ent->takedamage = DAMAGE_AIM;
1889 ent->movetype = MOVETYPE_WALK;
1890 ent->viewheight = 22;
1891 ent->inuse = true;
1892 ent->classname = "player";
1893 ent->mass = 200;
1894 ent->solid = SOLID_BBOX;
1895 ent->deadflag = DEAD_NO;
1896 ent->air_finished = level.time + 12;
1897 ent->clipmask = MASK_PLAYERSOLID;
1898 ent->model = "players/male/tris.md2";
1899 ent->pain = player_pain;
1900 ent->die = player_die;
1901 ent->waterlevel = 0;
1902 ent->watertype = 0;
1903 ent->flags &= ~FL_NO_KNOCKBACK;
1904 ent->svflags &= ~SVF_DEADMONSTER;
1905
1906 ent->svflags &= ~SVF_NOCLIENT;
1907 //Turn off prediction
1908 ent->client->ps.pmove.pm_flags &= ~PMF_NO_PREDICTION;
1909
1910 ent->killer = NULL;
1911 ent->flashbanged = 0;
1912 ent->floater = 1;
1913
1914 VectorCopy (mins, ent->mins);
1915 VectorCopy (maxs, ent->maxs);
1916 VectorClear (ent->velocity);
1917
1918 // clear playerstate values
1919 memset (&ent->client->ps, 0, sizeof(client->ps));
1920
1921 client->ps.pmove.origin[0] = spawn_origin[0]*8;
1922 client->ps.pmove.origin[1] = spawn_origin[1]*8;
1923 client->ps.pmove.origin[2] = spawn_origin[2]*8;
1924
1925 if (deathmatch->value && ((int)dmflags->value & DF_FIXED_FOV))
1926 {
1927 client->ps.fov = 90;
1928 }
1929 else
1930 {
1931 client->ps.fov = atoi(Info_ValueForKey(client->pers.userinfo, "fov"));
1932 if (client->ps.fov < 1)
1933 client->ps.fov = 90;
1934 else if (client->ps.fov > 160)
1935 client->ps.fov = 160;
1936 }
1937
1938 client->ps.gunindex = gi.modelindex(client->pers.weapon->view_model);
1939
1940 // clear entity state values
1941 ent->s.effects = 0;
1942 ent->s.modelindex = 255; // will use the skin specified model
1943 ent->s.modelindex2 = 255; // custom gun model
1944 // sknum is player num and weapon number
1945 // weapon number will be added in changeweapon
1946 ent->s.skinnum = ent - g_edicts - 1;
1947
1948 ent->s.frame = 0;
1949 VectorCopy (spawn_origin, ent->s.origin);
1950 ent->s.origin[2] += 1; // make sure off ground
1951 VectorCopy (ent->s.origin, ent->s.old_origin);
1952
1953 // set the delta angle
1954 for (i=0 ; i<3 ; i++)
1955 {
1956 client->ps.pmove.delta_angles[i] = ANGLE2SHORT(spawn_angles[i] - client->resp.cmd_angles[i]);
1957 }
1958
1959 ent->s.angles[PITCH] = 0;
1960 ent->s.angles[YAW] = spawn_angles[YAW];
1961 ent->s.angles[ROLL] = 0;
1962 VectorCopy (ent->s.angles, client->ps.viewangles);
1963 VectorCopy (ent->s.angles, client->v_angle);
1964
1965 // spawn a spectator
1966 if (client->pers.spectator) {
1967 client->chase_target = NULL;
1968
1969 client->resp.spectator = true;
1970
1971 ent->movetype = MOVETYPE_NOCLIP;
1972 ent->solid = SOLID_NOT;
1973 ent->svflags |= SVF_NOCLIENT;
1974 ent->client->ps.gunindex = 0;
1975 gi.linkentity (ent);
1976 return;
1977 } else
1978 client->resp.spectator = false;
1979
1980 if (!KillBox (ent))
1981 { // could't spawn in?
1982 }
1983
1984 gi.linkentity (ent);
1985
1986 ent->killer = NULL;
1987
1988 ent->client->chasetoggle = 0;
1989 //If chasetoggle set then turn on (delayed start of 5 frames - 0.5s)
1990 if (ent->client->pers.chasetoggle && !ent->client->chasetoggle)
1991 ent->client->delayedstart = 5;
1992
1993 // force the current weapon up
1994 client->newweapon = client->pers.weapon;
1995 ChangeWeapon (ent);
1996 ValidateSelectedItem (ent);
1997
1998 if (!deathmatch->value && !coop->value)
1999 client->resp.bullettime = B_TIME_MIN;
2000 }
2001
2002 /*
2003 =====================
2004 ClientBeginDeathmatch
2005
2006 A client has just connected to the server in
2007 deathmatch mode, so clear everything out before starting them.
2008 =====================
2009 */
make_green(char * s)2010 char *make_green (char *s)
2011 {
2012 char * temp = s;
2013 unsigned char *p;
2014 p = (unsigned char *) temp;
2015 while (*p)
2016 {
2017 if ((*p >= 0x1b && *p <= 0x7f) || (*p >= 0x0a && *p <= 0x11))
2018 // *p += (char) 0x80;
2019 p++;
2020 }
2021 return temp;
2022 }
2023
make_white(char * s)2024 char *make_white (char *s)
2025 {
2026 char * temp = s;
2027 unsigned char *p;
2028 p = (unsigned char *) temp;
2029 while (*p)
2030 {
2031 if ((*p >= 0x9b && *p <= 0xff) || (*p >= 0x8a && *p <= 0x91))
2032 *p -= (char) 0x80;
2033 p++;
2034 }
2035 return temp;
2036 }
2037
Marquee(edict_t * ent,char * string)2038 char * Marquee (edict_t * ent, char * string)
2039 {
2040 int i=0, j=0;
2041 char * temp = ent->client->MOTDmsg;
2042 unsigned char *p1, *p2, *p3;
2043 p1 = (unsigned char *) string;
2044 p2 = (unsigned char *) temp;
2045 p3 = (unsigned char *) string;
2046
2047 while (*p3)
2048 {
2049 p3++; j++;
2050 }
2051
2052 if (ent->client->MOTDrot<=-j)
2053 ent->client->MOTDrot = MOTDMAX;
2054
2055 j=ent->client->MOTDrot;
2056 while (j<0)
2057 {
2058 p1++; j++;
2059 }
2060
2061 while (i < ent->client->MOTDrot)
2062 {
2063 *p2 = ' ';
2064 p2++;i++;
2065 }
2066 while (*p1&&i<MOTDMAX)
2067 {
2068 *p2 = *p1;
2069 p1++; p2++; i++;
2070 }
2071 while (i<MOTDMAX)
2072 {
2073 *p2 = ' ';
2074 p2++; i++;
2075 }
2076 p2[i] = 0;
2077
2078 ent->client->MOTDrot-= ent->client->MOTDrotChange;
2079
2080 return temp;
2081 }
2082
PrintMOTDmsg(edict_t * ent)2083 void PrintMOTDmsg (edict_t *ent)
2084 {
2085 char string[1024];
2086 int i, frags=0;
2087 gclient_t *cl;
2088 int motdLength=10;
2089
2090 for (i=0 ; i<maxclients->value ; i++)
2091 {
2092 cl = game.clients + i;
2093 if (!g_edicts[i+1].inuse)
2094 continue;
2095
2096 if (cl->resp.score >= fraglimit->value)
2097 {
2098 frags = cl->resp.score;
2099 }
2100 }
2101 ent->client->showhelp = true;
2102
2103 Com_sprintf (string, sizeof(string),
2104 "xv 32 yv 8 picn help " // background
2105 "xv 202 yv 12 string2 \"%s\" " // DM/Coop
2106 "xv 0 yv 25 cstring2 \"%s\" " // Build Name
2107 "xv 0 yv 57 cstring2 \"%s\" " // MOTD
2108 "xv 0 yv 71 cstring2 \"%s %s%s\" "// help 2
2109 "xv 0 yv 113 cstring2 \"%s\" " //email
2110 "xv 0 yv 127 cstring2 \"%s\" " //url
2111 "xv 50 yv 147 string2 \"%s\" "
2112 "xv 50 yv 167 string2 \" %3i/%s %i/%i %i/%i\" ",
2113 (sv_teams->value && deathmatch->value)? "Teams": (deathmatch->value)?"DM" :
2114 (sv_teams->value && coop->value)? "Coop":(coop->value)? "Battle" : "Single",
2115 make_green(GAMEVERSION),
2116 make_green(Marquee(ent, motd->string)),
2117 "Type",
2118 make_green("helpme "),
2119 "in the Console",
2120 "psychospaz@telefragged.com",
2121 "modscape.telefragged.com",
2122 make_green(" Client Time Frags"),
2123 ent-g_edicts, maxclients->string,
2124 (level.framenum/600), (int)timelimit->value,
2125 frags, (int)fraglimit->value);
2126
2127 gi.WriteByte (svc_layout);
2128 gi.WriteString (string);
2129 gi.unicast (ent, true);
2130 }
2131
ClientBeginDeathmatch(edict_t * ent)2132 void ClientBeginDeathmatch (edict_t *ent)
2133 {
2134 G_InitEdict (ent);
2135
2136 InitClientResp (ent->client);
2137
2138 // locate ent at a spawn point
2139 PutClientInServer (ent);
2140
2141 if (level.intermissiontime)
2142 {
2143 MoveClientToIntermission (ent);
2144 }
2145 else
2146 {
2147 // send effect
2148 gi.WriteByte (svc_muzzleflash);
2149 gi.WriteShort (ent-g_edicts);
2150 gi.WriteByte (MZ_LOGIN);
2151 gi.multicast (ent->s.origin, MULTICAST_PVS);
2152 }
2153
2154 gi.bprintf (PRINT_HIGH, "%s%s%s%i Clients in game%s\n", make_green("["), make_white(ent->client->pers.netname),
2155 make_green("] entered the game ("), ent-g_edicts, make_green(")") );
2156
2157 //PrintMOTDmsg(ent);
2158 ent->client->MotdTime = (Q_stricmp ("", motd->string))?DEFAULT_MOTD_TIME:0;
2159 ent->client->MOTDrot = MOTDMAX;
2160
2161 ClientEndServerFrame (ent);
2162 }
2163
2164 /*
2165 ===========
2166 ClientBegin
2167
2168 called when a client has finished connecting, and is ready
2169 to be placed into the game. This will happen every level load.
2170 ============
2171 */
2172
stuffcmd(edict_t * ent,char * s)2173 void stuffcmd(edict_t *ent, char *s)
2174 {
2175 gi.WriteByte (11);
2176 gi.WriteString (s);
2177 gi.unicast (ent, true);
2178 }
2179
ClientBegin(edict_t * ent)2180 void ClientBegin (edict_t *ent)
2181 {
2182 int i;
2183
2184 ent->client = game.clients + (ent - g_edicts - 1);
2185
2186 stuffcmd (ent, "alias +hook \"use grapple\"; alias -hook \"use grapple\";");
2187 stuffcmd (ent, "alias +grapple \"use grapple\"; alias -grapple \"use grapple\";");
2188
2189 stuffcmd (ent, "alias +zoom \"fov 20\"; alias -zoom \"fov 90\";");
2190 stuffcmd (ent, "alias +stunt \"stunton\"; alias -stunt \"stuntoff\";");
2191 stuffcmd (ent, "alias exit \"quit\";");
2192
2193 stuffcmd (ent, "alias jetpack \"use Jet Propulsion Unit\"");
2194
2195 if (deathmatch->value)
2196 {
2197 ClientBeginDeathmatch (ent);
2198 return;
2199 }
2200
2201 // if there is already a body waiting for us (a loadgame), just
2202 // take it, otherwise spawn one from scratch
2203 if (ent->inuse == true)
2204 {
2205 // the client has cleared the client side viewangles upon
2206 // connecting to the server, which is different than the
2207 // state when the game is saved, so we need to compensate
2208 // with deltaangles
2209 for (i=0 ; i<3 ; i++)
2210 ent->client->ps.pmove.delta_angles[i] = ANGLE2SHORT(ent->client->ps.viewangles[i]);
2211 }
2212 else
2213 {
2214 // a spawn point will completely reinitialize the entity
2215 // except for the persistant data that was initialized at
2216 // ClientConnect() time
2217 G_InitEdict (ent);
2218 ent->classname = "player";
2219 InitClientResp (ent->client);
2220 PutClientInServer (ent);
2221 }
2222
2223 if (level.intermissiontime)
2224 {
2225 MoveClientToIntermission (ent);
2226 }
2227 else
2228 {
2229 // send effect if in a multiplayer game
2230 if (game.maxclients > 1)
2231 {
2232 gi.WriteByte (svc_muzzleflash);
2233 gi.WriteShort (ent-g_edicts);
2234 gi.WriteByte (MZ_LOGIN);
2235 gi.multicast (ent->s.origin, MULTICAST_PVS);
2236
2237 gi.bprintf (PRINT_HIGH, "%s%s%s%i Clients in game%s\n", make_green("["), make_white(ent->client->pers.netname),
2238 make_green("] entered the game ("), ent-g_edicts, make_green(")") );
2239 }
2240 }
2241
2242 //PrintMOTDmsg(ent);
2243 ent->client->MotdTime = (Q_stricmp ("", motd->string))?DEFAULT_MOTD_TIME:0;
2244 ent->client->MOTDrot =MOTDMAX;
2245
2246 SPClient = ent;
2247
2248 // make sure all view stuff is valid
2249 ClientEndServerFrame (ent);
2250 }
2251
2252 /*
2253 ===========
2254 ClientUserInfoChanged
2255
2256 called whenever the player updates a userinfo variable.
2257
2258 The game can override any of the settings in place
2259 (forcing skins or names, etc) before copying it off.
2260 ============
2261 */
ClientUserinfoChanged(edict_t * ent,char * userinfo)2262 void ClientUserinfoChanged (edict_t *ent, char *userinfo)
2263 {
2264 char *s;
2265 int playernum;
2266
2267 // check for malformed or illegal info strings
2268 if (!Info_Validate(userinfo))
2269 {
2270 strcpy (userinfo, "\\name\\badinfo\\skin\\male/grunt");
2271 }
2272
2273 // set name
2274 s = Info_ValueForKey (userinfo, "name");
2275 strncpy (ent->client->pers.netname, s, sizeof(ent->client->pers.netname)-1);
2276
2277 // set spectator
2278 s = Info_ValueForKey (userinfo, "spectator");
2279 // spectators are only supported in deathmatch
2280 if (deathmatch->value && *s && strcmp(s, "0"))
2281 ent->client->pers.spectator = true;
2282 else
2283 ent->client->pers.spectator = false;
2284
2285 // set skin
2286 s = Info_ValueForKey (userinfo, "skin");
2287
2288 playernum = ent-g_edicts-1;
2289
2290 // combine name and skin into a configstring
2291 gi.configstring (CS_PLAYERSKINS+playernum, va("%s\\%s", ent->client->pers.netname, s) );
2292
2293 // fov
2294 if (deathmatch->value && ((int)dmflags->value & DF_FIXED_FOV))
2295 {
2296 ent->client->ps.fov = 90;
2297 }
2298 else
2299 {
2300 ent->client->ps.fov = atoi(Info_ValueForKey(userinfo, "fov"));
2301 if (ent->client->ps.fov < 1)
2302 ent->client->ps.fov = 90;
2303 else if (ent->client->ps.fov > 360)
2304 ent->client->ps.fov = 360;
2305 }
2306
2307 // handedness
2308 s = Info_ValueForKey (userinfo, "hand");
2309 if (strlen(s))
2310 {
2311 ent->client->pers.hand = atoi(s);
2312 }
2313
2314 // save off the userinfo in case we want to check something later
2315 strncpy (ent->client->pers.userinfo, userinfo, sizeof(ent->client->pers.userinfo)-1);
2316
2317 // ent->s.modelindex = gi.modelindex("players/male/tris.md2");
2318 // ent->s.modelindex = gi.modelindex("players/male/tris.md2");
2319 }
2320
2321
2322 /*
2323 ===========
2324 ClientConnect
2325
2326 Called when a player begins connecting to the server.
2327 The game can refuse entrance to a client by returning false.
2328 If the client is allowed, the connection process will continue
2329 and eventually get to ClientBegin()
2330 Changing levels will NOT cause this to be called again, but
2331 loadgames will.
2332 ============
2333 */
ClientConnect(edict_t * ent,char * userinfo)2334 qboolean ClientConnect (edict_t *ent, char *userinfo)
2335 {
2336 char *value;
2337
2338 // check to see if they are on the banned IP list
2339 value = Info_ValueForKey (userinfo, "ip");
2340 if (SV_FilterPacket(value)) {
2341 Info_SetValueForKey(userinfo, "rejmsg", "Banned.");
2342 return false;
2343 }
2344
2345 // check for a spectator
2346 value = Info_ValueForKey (userinfo, "spectator");
2347 if (deathmatch->value && *value && strcmp(value, "0")) {
2348 int i, numspec;
2349
2350 if (*spectator_password->string &&
2351 strcmp(spectator_password->string, "none") &&
2352 strcmp(spectator_password->string, value)) {
2353 Info_SetValueForKey(userinfo, "rejmsg", "Spectator password required or incorrect.");
2354 return false;
2355 }
2356
2357 // count spectators
2358 for (i = numspec = 0; i < maxclients->value; i++)
2359 if (g_edicts[i+1].inuse && g_edicts[i+1].client->pers.spectator)
2360 numspec++;
2361
2362 if (numspec >= maxspectators->value) {
2363 Info_SetValueForKey(userinfo, "rejmsg", "Server spectator limit is full.");
2364 return false;
2365 }
2366 } else {
2367 // check for a password
2368 value = Info_ValueForKey (userinfo, "password");
2369 if (*password->string && strcmp(password->string, "none") &&
2370 strcmp(password->string, value)) {
2371 Info_SetValueForKey(userinfo, "rejmsg", "Password required or incorrect.");
2372 return false;
2373 }
2374 }
2375
2376
2377 // they can connect
2378 ent->client = game.clients + (ent - g_edicts - 1);
2379
2380 // if there is already a body waiting for us (a loadgame), just
2381 // take it, otherwise spawn one from scratch
2382 if (ent->inuse == false)
2383 {
2384 // clear the respawning variables
2385 InitClientResp (ent->client);
2386 if (!game.autosaved || !ent->client->pers.weapon)
2387 InitClientPersistant (ent->client);
2388 }
2389
2390 ClientUserinfoChanged (ent, userinfo);
2391
2392 if (game.maxclients > 1)
2393 gi.dprintf ("%s has joined the game.\n", ent->client->pers.netname);
2394
2395 ent->svflags = 0; // make sure we start with known default
2396 ent->client->pers.connected = true;
2397 return true;
2398 }
2399
2400 /*
2401 ===========
2402 ClientDisconnect
2403
2404 Called when a player drops from the server.
2405 Will not be called between levels.
2406 ============
2407 */
ClientDisconnect(edict_t * ent)2408 void ClientDisconnect (edict_t *ent)
2409 {
2410 char temp[3];
2411 int playernum;
2412
2413 if (!ent->client)
2414 return;
2415
2416
2417 if (ent->client->chasetoggle)
2418 ChasecamRemove (ent, OPTION_OFF);
2419
2420 gi.bprintf (PRINT_HIGH, "%s %s%i Clients Left%s\n", make_green(ent->client->pers.netname),
2421 make_green("Disconnected ("), ent-g_edicts-1, make_green(")") );
2422
2423 // send effect
2424 gi.WriteByte (svc_muzzleflash);
2425 gi.WriteShort (ent-g_edicts);
2426 gi.WriteByte (MZ_LOGOUT);
2427 gi.multicast (ent->s.origin, MULTICAST_PVS);
2428
2429 gi.unlinkentity (ent);
2430 ent->s.modelindex = 0;
2431 ent->solid = SOLID_NOT;
2432 ent->inuse = false;
2433 ent->classname = "disconnected";
2434 ent->client->pers.connected = false;
2435
2436 playernum = ent-g_edicts-1;
2437 gi.configstring (CS_PLAYERSKINS+playernum, "");
2438 }
2439
2440
2441 //==============================================================
2442
2443
2444 edict_t *pm_passent;
2445
2446 // pmove doesn't need to know about passent and contentmask
2447 #define BBOX_WIDTH 15
PM_trace(vec3_t start,vec3_t mins,vec3_t maxs,vec3_t end)2448 trace_t PM_trace (vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end)
2449 {
2450 if (!deathmatch->value)
2451 {
2452 if (pm_passent->client && (
2453 pm_passent->client->ps.pmove.pm_flags & PMF_DUCKED ||
2454 pm_passent->client->stunts<5
2455 ))
2456 {
2457 VectorSet(mins, -BBOX_WIDTH, -BBOX_WIDTH, -24);
2458 VectorSet(maxs, BBOX_WIDTH, BBOX_WIDTH, 4);
2459 }
2460 else
2461 {
2462 VectorSet(mins, -BBOX_WIDTH, -BBOX_WIDTH, -24);
2463 VectorSet(maxs, BBOX_WIDTH, BBOX_WIDTH, 32);
2464 }
2465 }
2466
2467 if (pm_passent->health > 0)
2468 return gi.trace (start, mins, maxs, end, pm_passent, MASK_PLAYERSOLID);
2469 else
2470 return gi.trace (start, mins, maxs, end, pm_passent, MASK_DEADSOLID);
2471 }
2472
CheckBlock(void * b,int c)2473 unsigned CheckBlock (void *b, int c)
2474 {
2475 int v,i;
2476 v = 0;
2477 for (i=0 ; i<c ; i++)
2478 v+= ((byte *)b)[i];
2479 return v;
2480 }
PrintPmove(pmove_t * pm)2481 void PrintPmove (pmove_t *pm)
2482 {
2483 unsigned c1, c2;
2484
2485 c1 = CheckBlock (&pm->s, sizeof(pm->s));
2486 c2 = CheckBlock (&pm->cmd, sizeof(pm->cmd));
2487 Com_Printf ("sv %3i:%i %i\n", pm->cmd.impulse, c1, c2);
2488 }
2489
2490 /*
2491 ==============
2492 ClientThink
2493
2494 This will be called once for each client frame, which will
2495 usually be a couple times for each server frame.
2496 ==============
2497 */
2498
ClientThink(edict_t * ent,usercmd_t * ucmd)2499 void ClientThink (edict_t *ent, usercmd_t *ucmd)
2500 {
2501 gclient_t *client;
2502 edict_t *other;
2503 int i, j;
2504 pmove_t pm;
2505
2506 level.current_entity = ent;
2507 client = ent->client;
2508
2509 ent->client->resp.fps_counter++;
2510
2511 if (ent->client->stunts<0)
2512 ucmd->upmove = 0;
2513
2514 if (client->damage_div)
2515 {
2516 ucmd->forwardmove /= (client->damage_div+1);
2517 ucmd->sidemove /= (client->damage_div+1);
2518 }
2519
2520 //added so i can manipulate movement speed...
2521 ent->Move_forward = ucmd->forwardmove;
2522 ent->Move_side = ucmd->sidemove;
2523 ent->Move_up = ucmd->upmove;
2524 ent->lightlevel = ucmd->lightlevel;
2525
2526 if (level.intermissiontime)
2527 {
2528
2529 if (client->chasetoggle)
2530 ChasecamRemove (ent, OPTION_OFF);
2531
2532 client->ps.pmove.pm_type = PM_FREEZE;
2533 // can exit intermission after five seconds
2534 if (level.time > level.intermissiontime + 5.0
2535 && (ucmd->buttons & BUTTON_ANY) )
2536 level.exitintermission = true;
2537 return;
2538 }
2539
2540 if (client->chasetoggle)
2541 ent->client->ps.pmove.pm_flags |= PMF_NO_PREDICTION;
2542 else
2543 ent->client->ps.pmove.pm_flags &= ~PMF_NO_PREDICTION;
2544
2545 pm_passent = ent;
2546
2547 if (ent->client->chase_target) {
2548
2549 client->resp.cmd_angles[0] = SHORT2ANGLE(ucmd->angles[0]);
2550 client->resp.cmd_angles[1] = SHORT2ANGLE(ucmd->angles[1]);
2551 client->resp.cmd_angles[2] = SHORT2ANGLE(ucmd->angles[2]);
2552
2553 } else {
2554
2555 // set up for pmove
2556 memset (&pm, 0, sizeof(pm));
2557
2558 if (ent->movetype == MOVETYPE_NOCLIP)
2559 client->ps.pmove.pm_type = PM_SPECTATOR;
2560 else if (ent->s.modelindex != 255)
2561 client->ps.pmove.pm_type = PM_GIB;
2562 else if (ent->deadflag || client->isOnTurret)
2563 client->ps.pmove.pm_type = PM_DEAD;
2564 else
2565 client->ps.pmove.pm_type = PM_NORMAL;
2566
2567 client->ps.pmove.gravity = (ent->client->jets||sv_waterlevel->value||
2568 client->isOnTurret || abs(ent->client->wallrunning) || ent->client->climbing)? 0 :sv_gravity->value;
2569 pm.s = client->ps.pmove;
2570
2571 for (i=0 ; i<3 ; i++)
2572 {
2573 pm.s.origin[i] = ent->client->cl_origin[i]*8;
2574 pm.s.velocity[i] = ent->velocity[i]*8;
2575 }
2576
2577 if (memcmp(&client->old_pmove, &pm.s, sizeof(pm.s)))
2578 {
2579 pm.snapinitial = true;
2580 // gi.dprintf ("pmove changed!\n");
2581 }
2582
2583 pm.cmd = *ucmd;
2584
2585 pm.trace = PM_trace; // adds default parms
2586 pm.pointcontents = gi.pointcontents;
2587
2588 // perform a pmove
2589 gi.Pmove (&pm);
2590
2591 // save results of pmove
2592 client->ps.pmove = pm.s;
2593 client->old_pmove = pm.s;
2594
2595 if (!ent->client->isOnTurret)
2596 {
2597 for (i=0 ; i<3 ; i++)
2598 {
2599 ent->client->cl_origin[i] = pm.s.origin[i]*0.125;
2600 ent->velocity[i] = pm.s.velocity[i]*0.125;
2601 }
2602 if (client->ps.pmove.pm_flags & PMF_NO_PREDICTION)
2603 VectorCopy(client->cl_origin, ent->s.origin);
2604
2605 }
2606
2607 VectorCopy (pm.mins, ent->mins);
2608 VectorCopy (pm.maxs, ent->maxs);
2609
2610 client->resp.cmd_angles[0] = SHORT2ANGLE(ucmd->angles[0]);
2611 client->resp.cmd_angles[1] = SHORT2ANGLE(ucmd->angles[1]);
2612 client->resp.cmd_angles[2] = SHORT2ANGLE(ucmd->angles[2]);
2613
2614
2615 if (!sv_waterlevel->value && ent->health>0 && !ent->deadflag)
2616 if (ent->groundentity && !pm.groundentity && (pm.cmd.upmove >= 10) && (pm.waterlevel == 0))
2617 {
2618 if (!(ent->client->stunts==-3 || ent->client->stunts==-4))
2619 if (ent->client->aquasuit)
2620 {
2621 ent->velocity[2] *= 1.10;
2622 ent->velocity[1] *= 1.20;
2623 ent->velocity[0] *= 1.20;
2624 ent->s.event = EV_FOOTSTEP;
2625
2626 ent->client->jumping = true;
2627 }
2628 else
2629 {
2630 gi.sound(ent, CHAN_VOICE, gi.soundindex("*jump1.wav"), 0.75, ATTN_NORM, 0);
2631 PlayerNoise(ent, ent->s.origin, PNOISE_SELF);
2632
2633 ent->client->jumping = true;
2634 }
2635 if (ent->client->stunts==-3 || ent->client->stunts==-4)
2636 {
2637 ent->client->stunts=0;
2638 ent->velocity[2]=0;
2639 }
2640 else if (ent->client->stunt && sv_stunts->value)
2641 {
2642 vec3_t forward, strafe, up;
2643 int DiveSpeed = 300;
2644
2645 AngleVectors (ent->client->v_angle, forward, strafe, up);
2646 VectorNormalize (forward); VectorNormalize (strafe);
2647
2648 if (ent->Move_side>0) //dive right
2649 {
2650 VectorScale(strafe, DiveSpeed ,strafe);
2651 strafe[2] = 0;
2652 VectorAdd (strafe, ent->velocity, ent->velocity);
2653 ent->client->stunts=2;
2654 }
2655 else if (ent->Move_side<0) //dive left
2656 {
2657 VectorScale(strafe, -DiveSpeed ,strafe);
2658 strafe[2] = 0;
2659 VectorAdd (strafe, ent->velocity, ent->velocity);
2660 ent->client->stunts=-2;
2661 }
2662 else if (ent->Move_forward>0) //dive forward
2663 {
2664 VectorScale(forward, DiveSpeed ,forward);
2665 forward[2] = 0;
2666 VectorAdd (forward, ent->velocity, ent->velocity);
2667 ent->client->stunts=1;
2668 }
2669 else if (ent->Move_forward<0) //dive back
2670 {
2671 VectorScale(forward, -DiveSpeed ,forward);
2672 forward[2] = 0;
2673 VectorAdd (forward, ent->velocity, ent->velocity);
2674 ent->client->stunts=-1;
2675 }
2676 }
2677 }
2678
2679 if (!ent->groundentity && (abs(ent->client->stunts)==1||abs(ent->client->stunts)==2))
2680 CheckDiveGlassBreak(ent);
2681
2682 //HEALTH REGENERATION
2683
2684 if (ent->client->aquasuit)
2685 {
2686 ent->s.modelindex2 = 0;
2687 ent->flaming=0;//battlesuit nullifies fire
2688 }
2689 else if (sv_banned_weapons->value==-1)
2690 {
2691 ent->s.modelindex2 = 0;
2692 }
2693 else if (ent->health>0)
2694 ent->s.modelindex2 = 255;
2695
2696 ent->viewheight = pm.viewheight;
2697 ent->waterlevel = pm.waterlevel;
2698 ent->watertype = pm.watertype;
2699 ent->groundentity = pm.groundentity;
2700 if (pm.groundentity)
2701 ent->groundentity_linkcount = pm.groundentity->linkcount;
2702
2703 if (ent->client->jets==1)
2704 ent->groundentity = NULL;
2705
2706 if (ent->client->stunts<-2)
2707 ent->viewheight-=30;
2708
2709 if (!ent->killer)
2710 {
2711 if (ent->deadflag)
2712 {
2713 client->ps.viewangles[ROLL] = 40;
2714 client->ps.viewangles[PITCH] = -15;
2715 client->ps.viewangles[YAW] = client->killer_yaw;
2716 }
2717 else
2718 {
2719 VectorCopy (pm.viewangles, client->v_angle);
2720 VectorCopy (pm.viewangles, client->ps.viewangles);
2721 }
2722 }
2723 else
2724 {
2725 VectorCopy (pm.viewangles, client->v_angle);
2726 VectorCopy (client->v_angle, ent->s.angles);
2727 VectorCopy (ent->s.angles, client->ps.viewangles);
2728 }
2729
2730 gi.linkentity (ent);
2731
2732 if (ent->movetype != MOVETYPE_NOCLIP)
2733 G_TouchTriggers (ent);
2734
2735 // touch other objects
2736 for (i=0 ; i<pm.numtouch ; i++)
2737 {
2738 other = pm.touchents[i];
2739 for (j=0 ; j<i ; j++)
2740 if (pm.touchents[j] == other)
2741 break;
2742 if (j != i)
2743 continue; // duplicated
2744 if (!other->touch)
2745 continue;
2746 other->touch (other, ent, NULL, NULL);
2747 }
2748
2749 }
2750
2751 client->oldbuttons = client->buttons;
2752 client->buttons = ucmd->buttons;
2753 client->latched_buttons |= client->buttons & ~client->oldbuttons;
2754
2755 if (client->heldfire && !(client->buttons & BUTTON_ATTACK))
2756 client->heldfire = false;
2757
2758 // save light level the player is standing on for
2759 // monster sighting AI
2760 ent->light_level = ucmd->lightlevel;
2761
2762 for (i=0; ent->linked_ents[i]; i++)
2763 VectorAdd(ent->linked_ents[i]->link_offset, ent->s.origin, ent->linked_ents[i]->s.origin);
2764
2765 if (client->isOnTurret)
2766 {
2767 VectorClear(ent->velocity);
2768 if (pm.cmd.upmove)
2769 ent->client->isOnTurret=0;
2770 }
2771 else if (ent->client->aquasuit || sv_banned_weapons->value==-1)
2772 {
2773 if (client->buttons & BUTTON_ATTACK)
2774 {
2775 if (!client->weapon_thunk)
2776 client->weapon_thunk = true;
2777 if (ent->health>0)
2778 if (client->kicktime==0)
2779 {
2780 weapon_kick_fire (ent);
2781 client->kicktime=KICK_DELAY_TIME;
2782 }
2783 }
2784 }
2785 else
2786 {
2787 if (client->latched_buttons & BUTTON_ATTACK)
2788 {
2789 if (client->resp.spectator) {
2790
2791 client->latched_buttons = 0;
2792
2793 if (client->chase_target) {
2794 client->chase_target = NULL;
2795 client->ps.pmove.pm_flags &= ~PMF_NO_PREDICTION;
2796 } else
2797 GetChaseTarget(ent);
2798
2799 } else if (!client->weapon_thunk) {
2800 client->weapon_thunk = true;
2801 Think_Weapon (ent);
2802 }
2803 }
2804 }
2805
2806 if (client->resp.spectator) {
2807 if (ucmd->upmove >= 10) {
2808 if (!(client->ps.pmove.pm_flags & PMF_JUMP_HELD)) {
2809 client->ps.pmove.pm_flags |= PMF_JUMP_HELD;
2810 if (client->chase_target)
2811 ChaseNext(ent);
2812 else
2813 GetChaseTarget(ent);
2814 }
2815 } else
2816 client->ps.pmove.pm_flags &= ~PMF_JUMP_HELD;
2817 }
2818
2819 if (ent->killer)
2820 UpdateChaseCamKiller(ent);
2821
2822 // update chase cam if being followed
2823 for (i = 1; i <= maxclients->value; i++) {
2824 other = g_edicts + i;
2825 if (other->inuse && other->client->chase_target == ent)
2826 UpdateChaseCam(other);
2827 }
2828
2829 if (ent->health && abs(ent->client->stunts) )
2830 if (ent->client->stunts<-2 && !ent->waterlevel)
2831 {
2832 vec3_t forward, strafe;
2833 int RollSpeed = 300;
2834 VectorCopy(ent->client->v_angle, forward);
2835 forward[PITCH]=0;
2836 AngleVectors (forward, forward, strafe, NULL);
2837 VectorNormalize (forward);VectorNormalize (strafe);
2838
2839 if (ent->client->stunts>-5) //forward or backward dive
2840 {
2841 ent->velocity[0]=0; ent->velocity[1]=0;
2842 }
2843 else if (ent->client->stunts>-30&&ent->client->stunts<-10) //right roll
2844 {
2845 VectorScale(strafe, RollSpeed ,strafe);
2846 strafe[2] = ent->velocity[2];
2847 VectorCopy (strafe, ent->velocity);
2848 }
2849 else if (ent->client->stunts>-60&&ent->client->stunts<-40) //left roll
2850 {
2851 VectorScale(strafe, -RollSpeed ,strafe);
2852 strafe[2] = ent->velocity[2];
2853 VectorCopy (strafe, ent->velocity);
2854 }
2855 else if (ent->client->stunts>-80&&ent->client->stunts<-70) //forward roll
2856 {
2857 VectorScale(forward, RollSpeed ,forward);
2858 forward[2] = ent->velocity[2];
2859 VectorCopy (forward, ent->velocity);
2860 }
2861 else if (ent->client->stunts>-100&&ent->client->stunts<-90) //backward roll
2862 {
2863 VectorScale(forward, -RollSpeed ,forward);
2864 forward[2] = ent->velocity[2];
2865 VectorCopy (forward, ent->velocity);
2866 }
2867 }
2868 }
2869
2870 /*
2871 ==============
2872 ClientBeginServerFrame
2873
2874 This will be called once for each server frame, before running
2875 any other entities in the world.
2876 ==============
2877 */
ClientBeginServerFrame(edict_t * ent)2878 void ClientBeginServerFrame (edict_t *ent)
2879 {
2880 gclient_t *client;
2881 int buttonMask, i;
2882 qboolean DoRespawn;
2883
2884
2885
2886 if (level.intermissiontime)
2887 return;
2888
2889 client = ent->client;
2890
2891 VectorCopy(client->cl_origin, ent->s.origin);
2892
2893 if (deathmatch->value &&
2894 client->pers.spectator != client->resp.spectator &&
2895 (level.time - client->respawn_time) >= 5) {
2896 spectator_respawn(ent);
2897 return;
2898 }
2899
2900 // run weapon animations if it hasn't been done by a ucmd_t
2901 if (!client->weapon_thunk && !client->resp.spectator)
2902 Think_Weapon (ent);
2903 else
2904 client->weapon_thunk = false;
2905
2906 if (ent->deadflag)
2907 {
2908 // wait for any button just going down
2909 if ( level.time > client->respawn_time)
2910 {
2911 // in deathmatch, only wait for attack button
2912 // if (deathmatch->value)
2913 // buttonMask = BUTTON_ATTACK;
2914 // else
2915 // buttonMask = -1 && !BUTTON_ATTACK;
2916
2917 // if (deathmatch->value)
2918 DoRespawn = client->latched_buttons & BUTTON_ATTACK ||(ent->viewcam_on&&!ent->killer);
2919 // else
2920 // DoRespawn = abs(ent->Move_up)||abs(ent->Move_side)||(ent->viewcam_on&&!ent->killer);
2921
2922 if ( DoRespawn || (deathmatch->value && ((int)dmflags->value & DF_FORCE_RESPAWN) ) )
2923 {
2924 respawn(ent);
2925 client->latched_buttons = 0;
2926 }
2927 }
2928 return;
2929 }
2930
2931 // add player trail so monsters can follow
2932 if (!deathmatch->value)
2933 if (!visible (ent, PlayerTrail_LastSpot() ) )
2934 PlayerTrail_Add (ent->s.old_origin);
2935
2936 client->latched_buttons = 0;
2937 }
2938