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