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