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