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