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