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