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