|
|
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: /*QUAKED info_player_coop_lava (1 0 1) (-16 -16 -24) (16 16 32)
148: potential spawning position for coop games on rmine2 where lava level
149: needs to be checked
150: */
151: void SP_info_player_coop_lava(edict_t *self)
152: {
153: if (!coop->value)
154: {
155: G_FreeEdict (self);
156: return;
157: }
158: }
159:
160: /*QUAKED info_player_intermission (1 0 1) (-16 -16 -24) (16 16 32)
161: The deathmatch intermission point will be at one of these
162: Use 'angles' instead of 'angle', so you can set pitch or roll as well as yaw. 'pitch yaw roll'
163: */
164: void SP_info_player_intermission(void)
165: {
166: }
167:
168:
169: //=======================================================================
170:
171:
172: void player_pain (edict_t *self, edict_t *other, float kick, int damage)
173: {
174: // player pain is handled at the end of the frame in P_DamageFeedback
175: }
176:
177:
178: qboolean IsFemale (edict_t *ent)
179: {
180: char *info;
181:
182: if (!ent->client)
183: return false;
184:
185: info = Info_ValueForKey (ent->client->pers.userinfo, "gender");
186: if (info[0] == 'f' || info[0] == 'F')
187: return true;
188: return false;
189: }
190:
191: qboolean IsNeutral (edict_t *ent)
192: {
193: char *info;
194:
195: if (!ent->client)
196: return false;
197:
198: info = Info_ValueForKey (ent->client->pers.userinfo, "gender");
199: if (info[0] != 'f' && info[0] != 'F' && info[0] != 'm' && info[0] != 'M')
200: return true;
201: return false;
202: }
203:
204: void ClientObituary (edict_t *self, edict_t *inflictor, edict_t *attacker)
205: {
206: int mod;
207: char *message;
208: char *message2;
209: qboolean ff;
210:
211: if (coop->value && attacker->client)
212: meansOfDeath |= MOD_FRIENDLY_FIRE;
213:
214: if (deathmatch->value || coop->value)
215: {
216: ff = meansOfDeath & MOD_FRIENDLY_FIRE;
217: mod = meansOfDeath & ~MOD_FRIENDLY_FIRE;
218: message = NULL;
219: message2 = "";
220:
221: switch (mod)
222: {
223: case MOD_SUICIDE:
224: message = "suicides";
225: break;
226: case MOD_FALLING:
227: message = "cratered";
228: break;
229: case MOD_CRUSH:
230: message = "was squished";
231: break;
232: case MOD_WATER:
233: message = "sank like a rock";
234: break;
235: case MOD_SLIME:
236: message = "melted";
237: break;
238: case MOD_LAVA:
239: message = "does a back flip into the lava";
240: break;
241: case MOD_EXPLOSIVE:
242: case MOD_BARREL:
243: message = "blew up";
244: break;
245: case MOD_EXIT:
246: message = "found a way out";
247: break;
248: case MOD_TARGET_LASER:
249: message = "saw the light";
250: break;
251: case MOD_TARGET_BLASTER:
252: message = "got blasted";
253: break;
254: case MOD_BOMB:
255: case MOD_SPLASH:
256: case MOD_TRIGGER_HURT:
257: message = "was in the wrong place";
258: break;
259: }
260: if (attacker == self)
261: {
262: switch (mod)
263: {
264: case MOD_HELD_GRENADE:
265: message = "tried to put the pin back in";
266: break;
267: case MOD_HG_SPLASH:
268: case MOD_G_SPLASH:
269: if (IsNeutral(self))
270: message = "tripped on its own grenade";
271: else if (IsFemale(self))
272: message = "tripped on her own grenade";
273: else
274: message = "tripped on his own grenade";
275: break;
276: case MOD_R_SPLASH:
277: if (IsNeutral(self))
278: message = "blew itself up";
279: else if (IsFemale(self))
280: message = "blew herself up";
281: else
282: message = "blew himself up";
283: break;
284: case MOD_BFG_BLAST:
285: message = "should have used a smaller gun";
286: break;
287: //ROGUE
288: case MOD_DOPPLE_EXPLODE:
289: if (IsNeutral(self))
290: message = "got caught in it's own trap";
291: else if (IsFemale(self))
292: message = "got caught in her own trap";
293: else
294: message = "got caught in his own trap";
295: break;
296: //ROGUE
297: default:
298: if (IsNeutral(self))
299: message = "killed itself";
300: else if (IsFemale(self))
301: message = "killed herself";
302: else
303: message = "killed himself";
304: break;
305: }
306: }
307: if (message)
308: {
309: gi.bprintf (PRINT_MEDIUM, "%s %s.\n", self->client->pers.netname, message);
310: if (deathmatch->value)
311: self->client->resp.score--;
312: self->enemy = NULL;
313: return;
314: }
315:
316: self->enemy = attacker;
317: if (attacker && attacker->client)
318: {
319: switch (mod)
320: {
321: case MOD_BLASTER:
322: message = "was blasted by";
323: break;
324: case MOD_SHOTGUN:
325: message = "was gunned down by";
326: break;
327: case MOD_SSHOTGUN:
328: message = "was blown away by";
329: message2 = "'s super shotgun";
330: break;
331: case MOD_MACHINEGUN:
332: message = "was machinegunned by";
333: break;
334: case MOD_CHAINGUN:
335: message = "was cut in half by";
336: message2 = "'s chaingun";
337: break;
338: case MOD_GRENADE:
339: message = "was popped by";
340: message2 = "'s grenade";
341: break;
342: case MOD_G_SPLASH:
343: message = "was shredded by";
344: message2 = "'s shrapnel";
345: break;
346: case MOD_ROCKET:
347: message = "ate";
348: message2 = "'s rocket";
349: break;
350: case MOD_R_SPLASH:
351: message = "almost dodged";
352: message2 = "'s rocket";
353: break;
354: case MOD_HYPERBLASTER:
355: message = "was melted by";
356: message2 = "'s hyperblaster";
357: break;
358: case MOD_RAILGUN:
359: message = "was railed by";
360: break;
361: case MOD_BFG_LASER:
362: message = "saw the pretty lights from";
363: message2 = "'s BFG";
364: break;
365: case MOD_BFG_BLAST:
366: message = "was disintegrated by";
367: message2 = "'s BFG blast";
368: break;
369: case MOD_BFG_EFFECT:
370: message = "couldn't hide from";
371: message2 = "'s BFG";
372: break;
373: case MOD_HANDGRENADE:
374: message = "caught";
375: message2 = "'s handgrenade";
376: break;
377: case MOD_HG_SPLASH:
378: message = "didn't see";
379: message2 = "'s handgrenade";
380: break;
381: case MOD_HELD_GRENADE:
382: message = "feels";
383: message2 = "'s pain";
384: break;
385: case MOD_TELEFRAG:
386: message = "tried to invade";
387: message2 = "'s personal space";
388: break;
389:
390: //===============
391: //ROGUE
392: case MOD_CHAINFIST:
393: message = "was shredded by";
394: message2 = "'s ripsaw";
395: break;
396: case MOD_DISINTEGRATOR:
397: message = "lost his grip courtesy of";
398: message2 = "'s disintegrator";
399: break;
400: case MOD_ETF_RIFLE:
401: message = "was perforated by";
402: break;
403: case MOD_HEATBEAM:
404: message = "was scorched by";
405: message2 = "'s plasma beam";
406: break;
407: case MOD_TESLA:
408: message = "was enlightened by";
409: message2 = "'s tesla mine";
410: break;
411: case MOD_PROX:
412: message = "got too close to";
413: message2 = "'s proximity mine";
414: break;
415: case MOD_NUKE:
416: message = "was nuked by";
417: message2 = "'s antimatter grenade";
418: break;
419: case MOD_VENGEANCE_SPHERE:
420: message = "was purged by";
421: message2 = "'s vengeance sphere";
422: break;
423: case MOD_DEFENDER_SPHERE:
424: message = "had a blast with";
425: message2 = "'s defender sphere";
426: break;
427: case MOD_HUNTER_SPHERE:
428: message = "was killed like a dog by";
429: message2 = "'s hunter sphere";
430: break;
431: case MOD_TRACKER:
432: message = "was annihilated by";
433: message2 = "'s disruptor";
434: break;
435: case MOD_DOPPLE_EXPLODE:
436: message = "was blown up by";
437: message2 = "'s doppleganger";
438: break;
439: case MOD_DOPPLE_VENGEANCE:
440: message = "was purged by";
441: message2 = "'s doppleganger";
442: break;
443: case MOD_DOPPLE_HUNTER:
444: message = "was hunted down by";
445: message2 = "'s doppleganger";
446: break;
447: //ROGUE
448: //===============
449: }
450: if (message)
451: {
452: gi.bprintf (PRINT_MEDIUM,"%s %s %s%s\n", self->client->pers.netname, message, attacker->client->pers.netname, message2);
453: //ROGUE
454: if (gamerules && gamerules->value)
455: {
456: if(DMGame.Score)
457: {
458: if(ff)
459: DMGame.Score(attacker, self, -1);
460: else
461: DMGame.Score(attacker, self, 1);
462: }
463: return;
464: }
465: //ROGUE
466:
467: if (deathmatch->value)
468: {
469: if (ff)
470: attacker->client->resp.score--;
471: else
472: attacker->client->resp.score++;
473: }
474: return;
475: }
476: }
477: }
478:
479: gi.bprintf (PRINT_MEDIUM,"%s died.\n", self->client->pers.netname);
480:
481: //ROGUE
482: if (g_showlogic && g_showlogic->value)
483: {
484: if (mod == MOD_UNKNOWN)
485: gi.dprintf ("Player killed by MOD_UNKNOWN\n");
486: else
487: gi.dprintf ("Player killed by undefined mod %d\n", mod);
488: }
489: //ROGUE
490:
491: if (deathmatch->value)
492: //ROGUE
493: {
494: if (gamerules && gamerules->value)
495: {
496: if(DMGame.Score)
497: {
498: DMGame.Score(self, self, -1);
499: }
500: return;
501: }
502: else
503: self->client->resp.score--;
504: }
505: //ROGUE
506:
507: }
508:
509:
510: void Touch_Item (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf);
511:
512: void TossClientWeapon (edict_t *self)
513: {
514: gitem_t *item;
515: edict_t *drop;
516: qboolean quad;
517: float spread;
518:
519: if (!deathmatch->value)
520: return;
521:
522: item = self->client->pers.weapon;
523: if (! self->client->pers.inventory[self->client->ammo_index] )
524: item = NULL;
525: if (item && (strcmp (item->pickup_name, "Blaster") == 0))
526: item = NULL;
527:
528: if (!((int)(dmflags->value) & DF_QUAD_DROP))
529: quad = false;
530: else
531: quad = (self->client->quad_framenum > (level.framenum + 10));
532:
533: if (item && quad)
534: spread = 22.5;
535: else
536: spread = 0.0;
537:
538: if (item)
539: {
540: self->client->v_angle[YAW] -= spread;
541: drop = Drop_Item (self, item);
542: self->client->v_angle[YAW] += spread;
543: drop->spawnflags = DROPPED_PLAYER_ITEM;
544: }
545:
546: if (quad)
547: {
548: self->client->v_angle[YAW] += spread;
549: drop = Drop_Item (self, FindItemByClassname ("item_quad"));
550: self->client->v_angle[YAW] -= spread;
551: drop->spawnflags |= DROPPED_PLAYER_ITEM;
552:
553: drop->touch = Touch_Item;
554: drop->nextthink = level.time + (self->client->quad_framenum - level.framenum) * FRAMETIME;
555: drop->think = G_FreeEdict;
556: }
557: }
558:
559:
560: /*
561: ==================
562: LookAtKiller
563: ==================
564: */
565: void LookAtKiller (edict_t *self, edict_t *inflictor, edict_t *attacker)
566: {
567: vec3_t dir;
568:
569: if (attacker && attacker != world && attacker != self)
570: {
571: VectorSubtract (attacker->s.origin, self->s.origin, dir);
572: }
573: else if (inflictor && inflictor != world && inflictor != self)
574: {
575: VectorSubtract (inflictor->s.origin, self->s.origin, dir);
576: }
577: else
578: {
579: self->client->killer_yaw = self->s.angles[YAW];
580: return;
581: }
582: // PMM - fixed to correct for pitch of 0
583: if (dir[0])
584: self->client->killer_yaw = 180/M_PI*atan2(dir[1], dir[0]);
585: else if (dir[1] > 0)
586: self->client->killer_yaw = 90;
587: else if (dir[1] < 0)
588: self->client->killer_yaw = 270;
589: else
590: self->client->killer_yaw = 0;
591: }
592:
593: /*
594: ==================
595: player_die
596: ==================
597: */
598: void player_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
599: {
600: int n;
601:
602: VectorClear (self->avelocity);
603:
604: self->takedamage = DAMAGE_YES;
605: self->movetype = MOVETYPE_TOSS;
606:
607: self->s.modelindex2 = 0; // remove linked weapon model
608:
609: self->s.angles[0] = 0;
610: self->s.angles[2] = 0;
611:
612: self->s.sound = 0;
613: self->client->weapon_sound = 0;
614:
615: self->maxs[2] = -8;
616:
617: // self->solid = SOLID_NOT;
618: self->svflags |= SVF_DEADMONSTER;
619:
620: if (!self->deadflag)
621: {
622: self->client->respawn_time = level.time + 1.0;
623: LookAtKiller (self, inflictor, attacker);
624: self->client->ps.pmove.pm_type = PM_DEAD;
625: ClientObituary (self, inflictor, attacker);
626: TossClientWeapon (self);
627: if (deathmatch->value)
628: Cmd_Help_f (self); // show scores
629:
630: // clear inventory
631: // this is kind of ugly, but it's how we want to handle keys in coop
632: for (n = 0; n < game.num_items; n++)
633: {
634: if (coop->value && itemlist[n].flags & IT_KEY)
635: self->client->resp.coop_respawn.inventory[n] = self->client->pers.inventory[n];
636: self->client->pers.inventory[n] = 0;
637: }
638: }
639:
640: if(gamerules && gamerules->value) // if we're in a dm game, alert the game
641: {
642: if(DMGame.PlayerDeath)
643: DMGame.PlayerDeath(self, inflictor, attacker);
644: }
645:
646: // remove powerups
647: self->client->quad_framenum = 0;
648: self->client->invincible_framenum = 0;
649: self->client->breather_framenum = 0;
650: self->client->enviro_framenum = 0;
651: self->flags &= ~FL_POWER_ARMOR;
652:
653: //==============
654: // ROGUE stuff
655: self->client->double_framenum = 0;
656:
657: // if there's a sphere around, let it know the player died.
658: // vengeance and hunter will die if they're not attacking,
659: // defender should always die
660: if(self->client->owned_sphere)
661: {
662: edict_t *sphere;
663:
664: sphere = self->client->owned_sphere;
665: sphere->die(sphere, self, self, 0, vec3_origin);
666: }
667:
668: // if we've been killed by the tracker, GIB!
669: if((meansOfDeath & ~MOD_FRIENDLY_FIRE) == MOD_TRACKER)
670: {
671: self->health = -100;
672: damage = 400;
673: }
674:
675: // make sure no trackers are still hurting us.
676: if(self->client->tracker_pain_framenum)
677: {
678: RemoveAttackingPainDaemons (self);
679: }
680:
681: // if we got obliterated by the nuke, don't gib
682: if ((self->health < -80) && (meansOfDeath == MOD_NUKE))
683: self->flags |= FL_NOGIB;
684:
685: // ROGUE
686: //==============
687:
688: if (self->health < -40)
689: {
690: // PMM
691: // don't toss gibs if we got vaped by the nuke
692: if (!(self->flags & FL_NOGIB))
693: {
694: // pmm
695: // gib
696: gi.sound (self, CHAN_BODY, gi.soundindex ("misc/udeath.wav"), 1, ATTN_NORM, 0);
697:
698: // more meaty gibs for your dollar!
699: if((deathmatch->value) && (self->health < -80))
700: {
701: for (n= 0; n < 4; n++)
702: ThrowGib (self, "models/objects/gibs/sm_meat/tris.md2", damage, GIB_ORGANIC);
703: }
704:
705: for (n= 0; n < 4; n++)
706: ThrowGib (self, "models/objects/gibs/sm_meat/tris.md2", damage, GIB_ORGANIC);
707: // PMM
708: }
709: self->flags &= ~FL_NOGIB;
710: // pmm
711:
712: ThrowClientHead (self, damage);
713:
714: self->takedamage = DAMAGE_NO;
715: }
716: else
717: { // normal death
718: if (!self->deadflag)
719: {
720: static int i;
721:
722: i = (i+1)%3;
723: // start a death animation
724: self->client->anim_priority = ANIM_DEATH;
725: if (self->client->ps.pmove.pm_flags & PMF_DUCKED)
726: {
727: self->s.frame = FRAME_crdeath1-1;
728: self->client->anim_end = FRAME_crdeath5;
729: }
730: else switch (i)
731: {
732: case 0:
733: self->s.frame = FRAME_death101-1;
734: self->client->anim_end = FRAME_death106;
735: break;
736: case 1:
737: self->s.frame = FRAME_death201-1;
738: self->client->anim_end = FRAME_death206;
739: break;
740: case 2:
741: self->s.frame = FRAME_death301-1;
742: self->client->anim_end = FRAME_death308;
743: break;
744: }
745: gi.sound (self, CHAN_VOICE, gi.soundindex(va("*death%i.wav", (rand()%4)+1)), 1, ATTN_NORM, 0);
746: }
747: }
748:
749: self->deadflag = DEAD_DEAD;
750:
751: gi.linkentity (self);
752: }
753:
754: //=======================================================================
755:
756: /*
757: ==============
758: InitClientPersistant
759:
760: This is only called when the game first initializes in single player,
761: but is called after each death and level change in deathmatch
762: ==============
763: */
764: void InitClientPersistant (gclient_t *client)
765: {
766: gitem_t *item;
767:
768: memset (&client->pers, 0, sizeof(client->pers));
769:
770: item = FindItem("Blaster");
771: client->pers.selected_item = ITEM_INDEX(item);
772: client->pers.inventory[client->pers.selected_item] = 1;
773:
774: client->pers.weapon = item;
775:
776: client->pers.health = 100;
777: client->pers.max_health = 100;
778:
779: client->pers.max_bullets = 200;
780: client->pers.max_shells = 100;
781: client->pers.max_rockets = 50;
782: client->pers.max_grenades = 50;
783: client->pers.max_cells = 200;
784: client->pers.max_slugs = 50;
785:
786: //ROGUE
787: // FIXME - give these real numbers....
788: client->pers.max_prox = 50;
789: client->pers.max_tesla = 50;
790: client->pers.max_flechettes = 200;
791: #ifndef KILL_DISRUPTOR
792: client->pers.max_rounds = 100;
793: #endif
794: //ROGUE
795:
796: client->pers.connected = true;
797: }
798:
799:
800: void InitClientResp (gclient_t *client)
801: {
802: memset (&client->resp, 0, sizeof(client->resp));
803: client->resp.enterframe = level.framenum;
804: client->resp.coop_respawn = client->pers;
805: }
806:
807: /*
808: ==================
809: SaveClientData
810:
811: Some information that should be persistant, like health,
812: is still stored in the edict structure, so it needs to
813: be mirrored out to the client structure before all the
814: edicts are wiped.
815: ==================
816: */
817: void SaveClientData (void)
818: {
819: int i;
820: edict_t *ent;
821:
822: for (i=0 ; i<game.maxclients ; i++)
823: {
824: ent = &g_edicts[1+i];
825: if (!ent->inuse)
826: continue;
827: game.clients[i].pers.health = ent->health;
828: game.clients[i].pers.max_health = ent->max_health;
829: game.clients[i].pers.savedFlags = (ent->flags & (FL_GODMODE|FL_NOTARGET|FL_POWER_ARMOR));
830: if (coop->value)
831: game.clients[i].pers.score = ent->client->resp.score;
832: }
833: }
834:
835: void FetchClientEntData (edict_t *ent)
836: {
837: ent->health = ent->client->pers.health;
838: ent->max_health = ent->client->pers.max_health;
839: ent->flags |= ent->client->pers.savedFlags;
840: if (coop->value)
841: ent->client->resp.score = ent->client->pers.score;
842: }
843:
844:
845:
846: /*
847: =======================================================================
848:
849: SelectSpawnPoint
850:
851: =======================================================================
852: */
853:
854: /*
855: ================
856: PlayersRangeFromSpot
857:
858: Returns the distance to the nearest player from the given spot
859: ================
860: */
861: float PlayersRangeFromSpot (edict_t *spot)
862: {
863: edict_t *player;
864: float bestplayerdistance;
865: vec3_t v;
866: int n;
867: float playerdistance;
868:
869:
870: bestplayerdistance = 9999999;
871:
872: for (n = 1; n <= maxclients->value; n++)
873: {
874: player = &g_edicts[n];
875:
876: if (!player->inuse)
877: continue;
878:
879: if (player->health <= 0)
880: continue;
881:
882: VectorSubtract (spot->s.origin, player->s.origin, v);
883: playerdistance = VectorLength (v);
884:
885: if (playerdistance < bestplayerdistance)
886: bestplayerdistance = playerdistance;
887: }
888:
889: return bestplayerdistance;
890: }
891:
892: /*
893: ================
894: SelectRandomDeathmatchSpawnPoint
895:
896: go to a random point, but NOT the two points closest
897: to other players
898: ================
899: */
900: edict_t *SelectRandomDeathmatchSpawnPoint (void)
901: {
902: edict_t *spot, *spot1, *spot2;
903: int count = 0;
904: int selection;
905: float range, range1, range2;
906:
907: spot = NULL;
908: range1 = range2 = 99999;
909: spot1 = spot2 = NULL;
910:
911: while ((spot = G_Find (spot, FOFS(classname), "info_player_deathmatch")) != NULL)
912: {
913: count++;
914: range = PlayersRangeFromSpot(spot);
915: if (range < range1)
916: {
917: range1 = range;
918: spot1 = spot;
919: }
920: else if (range < range2)
921: {
922: range2 = range;
923: spot2 = spot;
924: }
925: }
926:
927: if (!count)
928: return NULL;
929:
930: if (count <= 2)
931: {
932: spot1 = spot2 = NULL;
933: }
934: else
935: count -= 2;
936:
937: selection = rand() % count;
938:
939: spot = NULL;
940: do
941: {
942: spot = G_Find (spot, FOFS(classname), "info_player_deathmatch");
943: if (spot == spot1 || spot == spot2)
944: selection++;
945: } while(selection--);
946:
947: return spot;
948: }
949:
950: /*
951: ================
952: SelectFarthestDeathmatchSpawnPoint
953:
954: ================
955: */
956: edict_t *SelectFarthestDeathmatchSpawnPoint (void)
957: {
958: edict_t *bestspot;
959: float bestdistance, bestplayerdistance;
960: edict_t *spot;
961:
962:
963: spot = NULL;
964: bestspot = NULL;
965: bestdistance = 0;
966: while ((spot = G_Find (spot, FOFS(classname), "info_player_deathmatch")) != NULL)
967: {
968: bestplayerdistance = PlayersRangeFromSpot (spot);
969:
970: if (bestplayerdistance > bestdistance)
971: {
972: bestspot = spot;
973: bestdistance = bestplayerdistance;
974: }
975: }
976:
977: if (bestspot)
978: {
979: return bestspot;
980: }
981:
982: // if there is a player just spawned on each and every start spot
983: // we have no choice to turn one into a telefrag meltdown
984: spot = G_Find (NULL, FOFS(classname), "info_player_deathmatch");
985:
986: return spot;
987: }
988:
989: edict_t *SelectDeathmatchSpawnPoint (void)
990: {
991: if ( (int)(dmflags->value) & DF_SPAWN_FARTHEST)
992: return SelectFarthestDeathmatchSpawnPoint ();
993: else
994: return SelectRandomDeathmatchSpawnPoint ();
995: }
996:
997: //===============
998: //ROGUE
999: edict_t *SelectLavaCoopSpawnPoint (edict_t *ent)
1000: {
1001: int index;
1002: edict_t *spot = NULL;
1003: float lavatop;
1004: edict_t *lava;
1005: edict_t *pointWithLeastLava;
1006: float lowest;
1007: edict_t *spawnPoints [64];
1008: vec3_t center;
1009: int numPoints;
1010:
1011: // first, find the lava
1012: lava = NULL;
1013: while (1)
1014: {
1015: lava = G_Find (lava, FOFS(classname), "func_door");
1016: if(!lava)
1017: return NULL;
1018:
1019: VectorAdd (lava->absmax, lava->absmin, center);
1020: VectorScale (center, 0.5, center);
1021:
1022: if(lava->spawnflags & 2 && (gi.pointcontents(center) & MASK_WATER))
1023: break;
1024: }
1025:
1026: // find the top of the lava and include a small margin of error (plus bbox size)
1027: lavatop = lava->absmax[2] + 64;
1028:
1029: // find all the lava spawn points and store them in spawnPoints[]
1030: spot = NULL;
1031: numPoints = 0;
1032: while(spot = G_Find (spot, FOFS(classname), "info_player_coop_lava"))
1033: {
1034: if(numPoints == 64)
1035: break;
1036:
1037: spawnPoints[numPoints++] = spot;
1038: }
1039:
1040: if(numPoints < 1)
1041: return NULL;
1042:
1043: // walk up the sorted list and return the lowest, open, non-lava spawn point
1044: spot = NULL;
1045: lowest = 999999;
1046: pointWithLeastLava = NULL;
1047: for (index = 0; index < numPoints; index++)
1048: {
1049: if(spawnPoints[index]->s.origin[2] < lavatop)
1050: continue;
1051:
1052: if(PlayersRangeFromSpot(spawnPoints[index]) > 32)
1053: {
1054: if(spawnPoints[index]->s.origin[2] < lowest)
1055: {
1056: // save the last point
1057: pointWithLeastLava = spawnPoints[index];
1058: lowest = spawnPoints[index]->s.origin[2];
1059: }
1060: }
1061: }
1062:
1063: // FIXME - better solution????
1064: // well, we may telefrag someone, but oh well...
1065: if(pointWithLeastLava)
1066: return pointWithLeastLava;
1067:
1068: return NULL;
1069: }
1070: //ROGUE
1071: //===============
1072:
1073: edict_t *SelectCoopSpawnPoint (edict_t *ent)
1074: {
1075: int index;
1076: edict_t *spot = NULL;
1077: char *target;
1078:
1079: //ROGUE
1080: // rogue hack, but not too gross...
1081: if (!Q_stricmp(level.mapname, "rmine2p") || !Q_stricmp(level.mapname, "rmine2"))
1082: return SelectLavaCoopSpawnPoint (ent);
1083: //ROGUE
1084:
1085: index = ent->client - game.clients;
1086:
1087: // player 0 starts in normal player spawn point
1088: if (!index)
1089: return NULL;
1090:
1091: spot = NULL;
1092:
1093: // assume there are four coop spots at each spawnpoint
1094: while (1)
1095: {
1096: spot = G_Find (spot, FOFS(classname), "info_player_coop");
1097: if (!spot)
1098: return NULL; // we didn't have enough...
1099:
1100: target = spot->targetname;
1101: if (!target)
1102: target = "";
1103: if ( Q_stricmp(game.spawnpoint, target) == 0 )
1104: { // this is a coop spawn point for one of the clients here
1105: index--;
1106: if (!index)
1107: return spot; // this is it
1108: }
1109: }
1110:
1111:
1112: return spot;
1113: }
1114:
1115:
1116: /*
1117: ===========
1118: SelectSpawnPoint
1119:
1120: Chooses a player start, deathmatch start, coop start, etc
1121: ============
1122: */
1123: void SelectSpawnPoint (edict_t *ent, vec3_t origin, vec3_t angles)
1124: {
1125: edict_t *spot = NULL;
1126:
1127: if (deathmatch->value)
1128: spot = SelectDeathmatchSpawnPoint ();
1129: else if (coop->value)
1130: spot = SelectCoopSpawnPoint (ent);
1131:
1132: // find a single player start spot
1133: if (!spot)
1134: {
1135: while ((spot = G_Find (spot, FOFS(classname), "info_player_start")) != NULL)
1136: {
1137: if (!game.spawnpoint[0] && !spot->targetname)
1138: break;
1139:
1140: if (!game.spawnpoint[0] || !spot->targetname)
1141: continue;
1142:
1143: if (Q_stricmp(game.spawnpoint, spot->targetname) == 0)
1144: break;
1145: }
1146:
1147: if (!spot)
1148: {
1149: if (!game.spawnpoint[0])
1150: { // there wasn't a spawnpoint without a target, so use any
1151: spot = G_Find (spot, FOFS(classname), "info_player_start");
1152: }
1153: if (!spot)
1154: gi.error ("Couldn't find spawn point %s\n", game.spawnpoint);
1155: }
1156: }
1157:
1158: VectorCopy (spot->s.origin, origin);
1159: origin[2] += 9;
1160: VectorCopy (spot->s.angles, angles);
1161: }
1162:
1163: //======================================================================
1164:
1165:
1166: void InitBodyQue (void)
1167: {
1168: int i;
1169: edict_t *ent;
1170:
1171: level.body_que = 0;
1172: for (i=0; i<BODY_QUEUE_SIZE ; i++)
1173: {
1174: ent = G_Spawn();
1175: ent->classname = "bodyque";
1176: }
1177: }
1178:
1179: void body_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
1180: {
1181: int n;
1182:
1183: if (self->health < -40)
1184: {
1185: gi.sound (self, CHAN_BODY, gi.soundindex ("misc/udeath.wav"), 1, ATTN_NORM, 0);
1186: for (n= 0; n < 4; n++)
1187: ThrowGib (self, "models/objects/gibs/sm_meat/tris.md2", damage, GIB_ORGANIC);
1188: self->s.origin[2] -= 48;
1189: ThrowClientHead (self, damage);
1190: self->takedamage = DAMAGE_NO;
1191: }
1192: }
1193:
1194: void CopyToBodyQue (edict_t *ent)
1195: {
1196: edict_t *body;
1197:
1198: // grab a body que and cycle to the next one
1199: body = &g_edicts[(int)maxclients->value + level.body_que + 1];
1200: level.body_que = (level.body_que + 1) % BODY_QUEUE_SIZE;
1201:
1202: // FIXME: send an effect on the removed body
1203:
1204: gi.unlinkentity (ent);
1205:
1206: gi.unlinkentity (body);
1207: body->s = ent->s;
1208: body->s.number = body - g_edicts;
1209:
1210: body->svflags = ent->svflags;
1211: VectorCopy (ent->mins, body->mins);
1212: VectorCopy (ent->maxs, body->maxs);
1213: VectorCopy (ent->absmin, body->absmin);
1214: VectorCopy (ent->absmax, body->absmax);
1215: VectorCopy (ent->size, body->size);
1216: body->solid = ent->solid;
1217: body->clipmask = ent->clipmask;
1218: body->owner = ent->owner;
1219: body->movetype = ent->movetype;
1220:
1221: body->die = body_die;
1222: body->takedamage = DAMAGE_YES;
1223:
1224: gi.linkentity (body);
1225: }
1226:
1227:
1228: void respawn (edict_t *self)
1229: {
1230: if (deathmatch->value || coop->value)
1231: {
1232: // spectators don't leave bodies
1233: if (self->movetype != MOVETYPE_NOCLIP)
1234: CopyToBodyQue (self);
1235: self->svflags &= ~SVF_NOCLIENT;
1236: PutClientInServer (self);
1237:
1238: // add a teleportation effect
1239: self->s.event = EV_PLAYER_TELEPORT;
1240:
1241: // hold in place briefly
1242: self->client->ps.pmove.pm_flags = PMF_TIME_TELEPORT;
1243: self->client->ps.pmove.pm_time = 14;
1244:
1245: self->client->respawn_time = level.time;
1246:
1247: return;
1248: }
1249:
1250: // restart the entire server
1251: gi.AddCommandString ("menu_loadgame\n");
1252: }
1253:
1254: /*
1255: * only called when pers.spectator changes
1256: * note that resp.spectator should be the opposite of pers.spectator here
1257: */
1258: void spectator_respawn (edict_t *ent)
1259: {
1260: int i, numspec;
1261:
1262: // if the user wants to become a spectator, make sure he doesn't
1263: // exceed max_spectators
1264:
1265: if (ent->client->pers.spectator)
1266: {
1267: char *value = Info_ValueForKey (ent->client->pers.userinfo, "spectator");
1268: if (*spectator_password->string &&
1269: strcmp(spectator_password->string, "none") &&
1270: strcmp(spectator_password->string, value))
1271: {
1272: gi.cprintf(ent, PRINT_HIGH, "Spectator password incorrect.\n");
1273: ent->client->pers.spectator = false;
1274: gi.WriteByte (svc_stufftext);
1275: gi.WriteString ("spectator 0\n");
1276: gi.unicast(ent, true);
1277: return;
1278: }
1279:
1280: // count spectators
1281: for (i = 1, numspec = 0; i <= maxclients->value; i++)
1282: {
1283: if (g_edicts[i].inuse && g_edicts[i].client->pers.spectator)
1284: numspec++;
1285: }
1286:
1287: if (numspec >= maxspectators->value)
1288: {
1289: gi.cprintf(ent, PRINT_HIGH, "Server spectator limit is full.");
1290: ent->client->pers.spectator = false;
1291: // reset his spectator var
1292: gi.WriteByte (svc_stufftext);
1293: gi.WriteString ("spectator 0\n");
1294: gi.unicast(ent, true);
1295: return;
1296: }
1297: }
1298: else
1299: {
1300: // he was a spectator and wants to join the game
1301: // he must have the right password
1302: char *value = Info_ValueForKey (ent->client->pers.userinfo, "password");
1303: if (*password->string && strcmp(password->string, "none") &&
1304: strcmp(password->string, value))
1305: {
1306: gi.cprintf(ent, PRINT_HIGH, "Password incorrect.\n");
1307: ent->client->pers.spectator = true;
1308: gi.WriteByte (svc_stufftext);
1309: gi.WriteString ("spectator 1\n");
1310: gi.unicast(ent, true);
1311: return;
1312: }
1313: }
1314:
1315: // clear score on respawn
1316: ent->client->pers.score = ent->client->resp.score = 0;
1317:
1318: ent->svflags &= ~SVF_NOCLIENT;
1319: PutClientInServer (ent);
1320:
1321: // add a teleportation effect
1322: if (!ent->client->pers.spectator)
1323: {
1324: // send effect
1325: gi.WriteByte (svc_muzzleflash);
1326: gi.WriteShort (ent-g_edicts);
1327: gi.WriteByte (MZ_LOGIN);
1328: gi.multicast (ent->s.origin, MULTICAST_PVS);
1329:
1330: // hold in place briefly
1331: ent->client->ps.pmove.pm_flags = PMF_TIME_TELEPORT;
1332: ent->client->ps.pmove.pm_time = 14;
1333: }
1334:
1335: ent->client->respawn_time = level.time;
1336:
1337: if (ent->client->pers.spectator)
1338: gi.bprintf (PRINT_HIGH, "%s has moved to the sidelines\n", ent->client->pers.netname);
1339: else
1340: gi.bprintf (PRINT_HIGH, "%s joined the game\n", ent->client->pers.netname);
1341: }
1342:
1343: //==============================================================
1344:
1345:
1346: /*
1347: ===========
1348: PutClientInServer
1349:
1350: Called when a player connects to a server or respawns in
1351: a deathmatch.
1352: ============
1353: */
1354: void PutClientInServer (edict_t *ent)
1355: {
1356: vec3_t mins = {-16, -16, -24};
1357: vec3_t maxs = {16, 16, 32};
1358: int index;
1359: vec3_t spawn_origin, spawn_angles;
1360: gclient_t *client;
1361: int i;
1362: client_persistant_t saved;
1363: client_respawn_t resp;
1364:
1365: // find a spawn point
1366: // do it before setting health back up, so farthest
1367: // ranging doesn't count this client
1368: if(gamerules && gamerules->value && DMGame.SelectSpawnPoint) // PGM
1369: DMGame.SelectSpawnPoint (ent, spawn_origin, spawn_angles); // PGM
1370: else // PGM
1371: SelectSpawnPoint (ent, spawn_origin, spawn_angles);
1372:
1373: index = ent-g_edicts-1;
1374: client = ent->client;
1375:
1376: // deathmatch wipes most client data every spawn
1377: if (deathmatch->value)
1378: {
1379: char userinfo[MAX_INFO_STRING];
1380:
1381: resp = client->resp;
1382: memcpy (userinfo, client->pers.userinfo, sizeof(userinfo));
1383: InitClientPersistant (client);
1384: ClientUserinfoChanged (ent, userinfo);
1385: }
1386: else if (coop->value)
1387: {
1388: // int n;
1389: char userinfo[MAX_INFO_STRING];
1390:
1391: resp = client->resp;
1392: memcpy (userinfo, client->pers.userinfo, sizeof(userinfo));
1393: // this is kind of ugly, but it's how we want to handle keys in coop
1394: // for (n = 0; n < game.num_items; n++)
1395: // {
1396: // if (itemlist[n].flags & IT_KEY)
1397: // resp.coop_respawn.inventory[n] = client->pers.inventory[n];
1398: // }
1399: resp.coop_respawn.game_helpchanged = client->pers.game_helpchanged;
1400: resp.coop_respawn.helpchanged = client->pers.helpchanged;
1401: client->pers = resp.coop_respawn;
1402: ClientUserinfoChanged (ent, userinfo);
1403: if (resp.score > client->pers.score)
1404: client->pers.score = resp.score;
1405: }
1406: else
1407: {
1408: memset (&resp, 0, sizeof(resp));
1409: }
1410:
1411: // clear everything but the persistant data
1412: saved = client->pers;
1413: memset (client, 0, sizeof(*client));
1414: client->pers = saved;
1415: if (client->pers.health <= 0)
1416: InitClientPersistant(client);
1417: client->resp = resp;
1418:
1419: // copy some data from the client to the entity
1420: FetchClientEntData (ent);
1421:
1422: // clear entity values
1423: ent->groundentity = NULL;
1424: ent->client = &game.clients[index];
1425: ent->takedamage = DAMAGE_AIM;
1426: ent->movetype = MOVETYPE_WALK;
1427: ent->viewheight = 22;
1428: ent->inuse = true;
1429: ent->classname = "player";
1430: ent->mass = 200;
1431: ent->solid = SOLID_BBOX;
1432: ent->deadflag = DEAD_NO;
1433: ent->air_finished = level.time + 12;
1434: ent->clipmask = MASK_PLAYERSOLID;
1435: ent->model = "players/male/tris.md2";
1436: ent->pain = player_pain;
1437: ent->die = player_die;
1438: ent->waterlevel = 0;
1439: ent->watertype = 0;
1440: ent->flags &= ~FL_NO_KNOCKBACK;
1441: ent->svflags &= ~SVF_DEADMONSTER;
1442:
1443: ent->flags &= ~FL_SAM_RAIMI; // PGM - turn off sam raimi flag
1444:
1445: VectorCopy (mins, ent->mins);
1446: VectorCopy (maxs, ent->maxs);
1447: VectorClear (ent->velocity);
1448:
1449: // clear playerstate values
1450: memset (&ent->client->ps, 0, sizeof(client->ps));
1451:
1452: client->ps.pmove.origin[0] = spawn_origin[0]*8;
1453: client->ps.pmove.origin[1] = spawn_origin[1]*8;
1454: client->ps.pmove.origin[2] = spawn_origin[2]*8;
1455:
1456: if (deathmatch->value && ((int)dmflags->value & DF_FIXED_FOV))
1457: {
1458: client->ps.fov = 90;
1459: }
1460: else
1461: {
1462: client->ps.fov = atoi(Info_ValueForKey(client->pers.userinfo, "fov"));
1463: if (client->ps.fov < 1)
1464: client->ps.fov = 90;
1465: else if (client->ps.fov > 160)
1466: client->ps.fov = 160;
1467: }
1468:
1469: //PGM
1470: if (client->pers.weapon)
1471: client->ps.gunindex = gi.modelindex(client->pers.weapon->view_model);
1472: else
1473: client->ps.gunindex = 0;
1474: //PGM
1475:
1476: // clear entity state values
1477: ent->s.effects = 0;
1478: ent->s.modelindex = 255; // will use the skin specified model
1479: ent->s.modelindex2 = 255; // custom gun model
1480: // sknum is player num and weapon number
1481: // weapon number will be added in changeweapon
1482: ent->s.skinnum = ent - g_edicts - 1;
1483:
1484: ent->s.frame = 0;
1485: VectorCopy (spawn_origin, ent->s.origin);
1486: ent->s.origin[2] += 1; // make sure off ground
1487: VectorCopy (ent->s.origin, ent->s.old_origin);
1488:
1489: // set the delta angle
1490: for (i=0 ; i<3 ; i++)
1491: client->ps.pmove.delta_angles[i] = ANGLE2SHORT(spawn_angles[i] - client->resp.cmd_angles[i]);
1492:
1493: ent->s.angles[PITCH] = 0;
1494: ent->s.angles[YAW] = spawn_angles[YAW];
1495: ent->s.angles[ROLL] = 0;
1496: VectorCopy (ent->s.angles, client->ps.viewangles);
1497: VectorCopy (ent->s.angles, client->v_angle);
1498:
1499: // spawn a spectator
1500: if (client->pers.spectator)
1501: {
1502: client->chase_target = NULL;
1503:
1504: client->resp.spectator = true;
1505:
1506: ent->movetype = MOVETYPE_NOCLIP;
1507: ent->solid = SOLID_NOT;
1508: ent->svflags |= SVF_NOCLIENT;
1509: ent->client->ps.gunindex = 0;
1510: gi.linkentity (ent);
1511: return;
1512: }
1513: else
1514: client->resp.spectator = false;
1515:
1516: if (!KillBox (ent))
1517: { // could't spawn in?
1518: }
1519:
1520: gi.linkentity (ent);
1521:
1522: // force the current weapon up
1523: client->newweapon = client->pers.weapon;
1524: ChangeWeapon (ent);
1525: }
1526:
1527: /*
1528: =====================
1529: ClientBeginDeathmatch
1530:
1531: A client has just connected to the server in
1532: deathmatch mode, so clear everything out before starting them.
1533: =====================
1534: */
1535: void ClientBeginDeathmatch (edict_t *ent)
1536: {
1537: G_InitEdict (ent);
1538:
1539: InitClientResp (ent->client);
1540:
1541: //PGM
1542: if(gamerules && gamerules->value && DMGame.ClientBegin)
1543: {
1544: DMGame.ClientBegin (ent);
1545: }
1546: //PGM
1547:
1548: // locate ent at a spawn point
1549: PutClientInServer (ent);
1550:
1551: // send effect
1552: gi.WriteByte (svc_muzzleflash);
1553: gi.WriteShort (ent-g_edicts);
1554: gi.WriteByte (MZ_LOGIN);
1555: gi.multicast (ent->s.origin, MULTICAST_PVS);
1556:
1557: gi.bprintf (PRINT_HIGH, "%s entered the game\n", ent->client->pers.netname);
1558:
1559: // make sure all view stuff is valid
1560: ClientEndServerFrame (ent);
1561: }
1562:
1563:
1564: /*
1565: ===========
1566: ClientBegin
1567:
1568: called when a client has finished connecting, and is ready
1569: to be placed into the game. This will happen every level load.
1570: ============
1571: */
1572: void ClientBegin (edict_t *ent)
1573: {
1574: int i;
1575:
1576: ent->client = game.clients + (ent - g_edicts - 1);
1577:
1578: if (deathmatch->value)
1579: {
1580: ClientBeginDeathmatch (ent);
1581: return;
1582: }
1583:
1584: // if there is already a body waiting for us (a loadgame), just
1585: // take it, otherwise spawn one from scratch
1586: if (ent->inuse == true)
1587: {
1588: // the client has cleared the client side viewangles upon
1589: // connecting to the server, which is different than the
1590: // state when the game is saved, so we need to compensate
1591: // with deltaangles
1592: for (i=0 ; i<3 ; i++)
1593: ent->client->ps.pmove.delta_angles[i] = ANGLE2SHORT(ent->client->ps.viewangles[i]);
1594: }
1595: else
1596: {
1597: // a spawn point will completely reinitialize the entity
1598: // except for the persistant data that was initialized at
1599: // ClientConnect() time
1600: G_InitEdict (ent);
1601: ent->classname = "player";
1602: InitClientResp (ent->client);
1603: PutClientInServer (ent);
1604: }
1605:
1606: if (level.intermissiontime)
1607: {
1608: MoveClientToIntermission (ent);
1609: }
1610: else
1611: {
1612: // send effect if in a multiplayer game
1613: if (game.maxclients > 1)
1614: {
1615: gi.WriteByte (svc_muzzleflash);
1616: gi.WriteShort (ent-g_edicts);
1617: gi.WriteByte (MZ_LOGIN);
1618: gi.multicast (ent->s.origin, MULTICAST_PVS);
1619:
1620: gi.bprintf (PRINT_HIGH, "%s entered the game\n", ent->client->pers.netname);
1621: }
1622: }
1623:
1624: // make sure all view stuff is valid
1625: ClientEndServerFrame (ent);
1626: }
1627:
1628: /*
1629: ===========
1630: ClientUserInfoChanged
1631:
1632: called whenever the player updates a userinfo variable.
1633:
1634: The game can override any of the settings in place
1635: (forcing skins or names, etc) before copying it off.
1636: ============
1637: */
1638: void ClientUserinfoChanged (edict_t *ent, char *userinfo)
1639: {
1640: char *s;
1641: int playernum;
1642:
1643: // check for malformed or illegal info strings
1644: if (!Info_Validate(userinfo))
1645: {
1646: strcpy (userinfo, "\\name\\badinfo\\skin\\male/grunt");
1647: }
1648:
1649: // set name
1650: s = Info_ValueForKey (userinfo, "name");
1651: strncpy (ent->client->pers.netname, s, sizeof(ent->client->pers.netname)-1);
1652:
1653: // set spectator
1654: s = Info_ValueForKey (userinfo, "spectator");
1655: // spectators are only supported in deathmatch
1656: // if (deathmatch->value && strcmp(s, "0"))
1657: if (deathmatch->value && *s && strcmp(s, "0"))
1658: ent->client->pers.spectator = true;
1659: else
1660: ent->client->pers.spectator = false;
1661:
1662: // set skin
1663: s = Info_ValueForKey (userinfo, "skin");
1664:
1665: playernum = ent-g_edicts-1;
1666:
1667: // combine name and skin into a configstring
1668: gi.configstring (CS_PLAYERSKINS+playernum, va("%s\\%s", ent->client->pers.netname, s) );
1669:
1670: // fov
1671: if (deathmatch->value && ((int)dmflags->value & DF_FIXED_FOV))
1672: {
1673: ent->client->ps.fov = 90;
1674: }
1675: else
1676: {
1677: ent->client->ps.fov = atoi(Info_ValueForKey(userinfo, "fov"));
1678: if (ent->client->ps.fov < 1)
1679: ent->client->ps.fov = 90;
1680: else if (ent->client->ps.fov > 160)
1681: ent->client->ps.fov = 160;
1682: }
1683:
1684: // handedness
1685: s = Info_ValueForKey (userinfo, "hand");
1686: if (strlen(s))
1687: {
1688: ent->client->pers.hand = atoi(s);
1689: }
1690:
1691: // save off the userinfo in case we want to check something later
1692: strncpy (ent->client->pers.userinfo, userinfo, sizeof(ent->client->pers.userinfo)-1);
1693: }
1694:
1695:
1696: /*
1697: ===========
1698: ClientConnect
1699:
1700: Called when a player begins connecting to the server.
1701: The game can refuse entrance to a client by returning false.
1702: If the client is allowed, the connection process will continue
1703: and eventually get to ClientBegin()
1704: Changing levels will NOT cause this to be called again, but
1705: loadgames will.
1706: ============
1707: */
1708: qboolean ClientConnect (edict_t *ent, char *userinfo)
1709: {
1710: char *value;
1711:
1712: // check to see if they are on the banned IP list
1713: value = Info_ValueForKey (userinfo, "ip");
1714: if (SV_FilterPacket(value))
1715: {
1716: Info_SetValueForKey(userinfo, "rejmsg", "Banned.");
1717: return false;
1718: }
1719:
1720: // check for a spectator
1721: value = Info_ValueForKey (userinfo, "spectator");
1722: // if (deathmatch->value && strcmp(value, "0"))
1723: if (deathmatch->value && *value && strcmp(value, "0"))
1724: {
1725: int i, numspec;
1726:
1727: if (*spectator_password->string &&
1728: strcmp(spectator_password->string, "none") &&
1729: strcmp(spectator_password->string, value))
1730: {
1731: Info_SetValueForKey(userinfo, "rejmsg", "Spectator password required or incorrect.");
1732: return false;
1733: }
1734:
1735: // count spectators
1736: for (i = numspec = 0; i < maxclients->value; i++)
1737: {
1738: if (g_edicts[i+1].inuse && g_edicts[i+1].client->pers.spectator)
1739: numspec++;
1740: }
1741:
1742: if (numspec >= maxspectators->value)
1743: {
1744: Info_SetValueForKey(userinfo, "rejmsg", "Server spectator limit is full.");
1745: return false;
1746: }
1747: }
1748: else
1749: {
1750: // check for a password
1751: value = Info_ValueForKey (userinfo, "password");
1752: if (*password->string && strcmp(password->string, "none") &&
1753: strcmp(password->string, value))
1754: {
1755: Info_SetValueForKey(userinfo, "rejmsg", "Password required or incorrect.");
1756: return false;
1757: }
1758: }
1759:
1760:
1761: // they can connect
1762: ent->client = game.clients + (ent - g_edicts - 1);
1763:
1764: // if there is already a body waiting for us (a loadgame), just
1765: // take it, otherwise spawn one from scratch
1766: if (ent->inuse == false)
1767: {
1768: // clear the respawning variables
1769: InitClientResp (ent->client);
1770: if (!game.autosaved || !ent->client->pers.weapon)
1771: InitClientPersistant (ent->client);
1772: }
1773:
1774: ClientUserinfoChanged (ent, userinfo);
1775:
1776: if (game.maxclients > 1)
1777: gi.dprintf ("%s connected\n", ent->client->pers.netname);
1778:
1779: ent->client->pers.connected = true;
1780: return true;
1781: }
1782:
1783: /*
1784: ===========
1785: ClientDisconnect
1786:
1787: Called when a player drops from the server.
1788: Will not be called between levels.
1789: ============
1790: */
1791: void ClientDisconnect (edict_t *ent)
1792: {
1793: int playernum;
1794:
1795: if (!ent->client)
1796: return;
1797:
1798: gi.bprintf (PRINT_HIGH, "%s disconnected\n", ent->client->pers.netname);
1799:
1800: //============
1801: //ROGUE
1802: // make sure no trackers are still hurting us.
1803: if(ent->client->tracker_pain_framenum)
1804: RemoveAttackingPainDaemons (ent);
1805:
1806: if (ent->client->owned_sphere)
1807: {
1808: if(ent->client->owned_sphere->inuse)
1809: G_FreeEdict (ent->client->owned_sphere);
1810: ent->client->owned_sphere = NULL;
1811: }
1812:
1813: if (gamerules && gamerules->value)
1814: {
1815: if(DMGame.PlayerDisconnect)
1816: DMGame.PlayerDisconnect(ent);
1817: }
1818: //ROGUE
1819: //============
1820:
1821: // send effect
1822: gi.WriteByte (svc_muzzleflash);
1823: gi.WriteShort (ent-g_edicts);
1824: gi.WriteByte (MZ_LOGOUT);
1825: gi.multicast (ent->s.origin, MULTICAST_PVS);
1826:
1827: gi.unlinkentity (ent);
1828: ent->s.modelindex = 0;
1829: ent->solid = SOLID_NOT;
1830: ent->inuse = false;
1831: ent->classname = "disconnected";
1832: ent->client->pers.connected = false;
1833:
1834: playernum = ent-g_edicts-1;
1835: gi.configstring (CS_PLAYERSKINS+playernum, "");
1836: }
1837:
1838:
1839: //==============================================================
1840:
1841:
1842: edict_t *pm_passent;
1843:
1844: // pmove doesn't need to know about passent and contentmask
1845: trace_t PM_trace (vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end)
1846: {
1847: if (pm_passent->health > 0)
1848: return gi.trace (start, mins, maxs, end, pm_passent, MASK_PLAYERSOLID);
1849: else
1850: return gi.trace (start, mins, maxs, end, pm_passent, MASK_DEADSOLID);
1851: }
1852:
1853: unsigned CheckBlock (void *b, int c)
1854: {
1855: int v,i;
1856: v = 0;
1857: for (i=0 ; i<c ; i++)
1858: v+= ((byte *)b)[i];
1859: return v;
1860: }
1861: void PrintPmove (pmove_t *pm)
1862: {
1863: unsigned c1, c2;
1864:
1865: c1 = CheckBlock (&pm->s, sizeof(pm->s));
1866: c2 = CheckBlock (&pm->cmd, sizeof(pm->cmd));
1867: Com_Printf ("sv %3i:%i %i\n", pm->cmd.impulse, c1, c2);
1868: }
1869:
1870: /*
1871: ==============
1872: ClientThink
1873:
1874: This will be called once for each client frame, which will
1875: usually be a couple times for each server frame.
1876: ==============
1877: */
1878: void ClientThink (edict_t *ent, usercmd_t *ucmd)
1879: {
1880: gclient_t *client;
1881: edict_t *other;
1882: int i, j;
1883: pmove_t pm;
1884:
1885: level.current_entity = ent;
1886: client = ent->client;
1887:
1888: if (level.intermissiontime)
1889: {
1890: client->ps.pmove.pm_type = PM_FREEZE;
1891: // can exit intermission after five seconds
1892: if (level.time > level.intermissiontime + 5.0
1893: && (ucmd->buttons & BUTTON_ANY) )
1894: level.exitintermission = true;
1895: return;
1896: }
1897:
1898: pm_passent = ent;
1899:
1900: if (ent->client->chase_target)
1901: {
1902: client->resp.cmd_angles[0] = SHORT2ANGLE(ucmd->angles[0]);
1903: client->resp.cmd_angles[1] = SHORT2ANGLE(ucmd->angles[1]);
1904: client->resp.cmd_angles[2] = SHORT2ANGLE(ucmd->angles[2]);
1905: }
1906: else
1907: {
1908: // set up for pmove
1909: memset (&pm, 0, sizeof(pm));
1910:
1911: if (ent->movetype == MOVETYPE_NOCLIP)
1912: client->ps.pmove.pm_type = PM_SPECTATOR;
1913: else if (ent->s.modelindex != 255)
1914: client->ps.pmove.pm_type = PM_GIB;
1915: else if (ent->deadflag)
1916: client->ps.pmove.pm_type = PM_DEAD;
1917: else
1918: client->ps.pmove.pm_type = PM_NORMAL;
1919:
1920: //PGM trigger_gravity support
1921: // client->ps.pmove.gravity = sv_gravity->value;
1922: client->ps.pmove.gravity = sv_gravity->value * ent->gravity;
1923: //PGM
1924: pm.s = client->ps.pmove;
1925:
1926: for (i=0 ; i<3 ; i++)
1927: {
1928: pm.s.origin[i] = ent->s.origin[i]*8;
1929: pm.s.velocity[i] = ent->velocity[i]*8;
1930: }
1931:
1932: if (memcmp(&client->old_pmove, &pm.s, sizeof(pm.s)))
1933: {
1934: pm.snapinitial = true;
1935: // gi.dprintf ("pmove changed!\n");
1936: }
1937:
1938: pm.cmd = *ucmd;
1939:
1940: pm.trace = PM_trace; // adds default parms
1941: pm.pointcontents = gi.pointcontents;
1942:
1943: // perform a pmove
1944: gi.Pmove (&pm);
1945:
1946: // save results of pmove
1947: client->ps.pmove = pm.s;
1948: client->old_pmove = pm.s;
1949:
1950: for (i=0 ; i<3 ; i++)
1951: {
1952: ent->s.origin[i] = pm.s.origin[i]*0.125;
1953: ent->velocity[i] = pm.s.velocity[i]*0.125;
1954: }
1955:
1956: VectorCopy (pm.mins, ent->mins);
1957: VectorCopy (pm.maxs, ent->maxs);
1958:
1959: client->resp.cmd_angles[0] = SHORT2ANGLE(ucmd->angles[0]);
1960: client->resp.cmd_angles[1] = SHORT2ANGLE(ucmd->angles[1]);
1961: client->resp.cmd_angles[2] = SHORT2ANGLE(ucmd->angles[2]);
1962:
1963: if (ent->groundentity && !pm.groundentity && (pm.cmd.upmove >= 10) && (pm.waterlevel == 0))
1964: {
1965: gi.sound(ent, CHAN_VOICE, gi.soundindex("*jump1.wav"), 1, ATTN_NORM, 0);
1966: PlayerNoise(ent, ent->s.origin, PNOISE_SELF);
1967: }
1968:
1969: //ROGUE sam raimi cam support
1970: if(ent->flags & FL_SAM_RAIMI)
1971: ent->viewheight = 8;
1972: else
1973: ent->viewheight = pm.viewheight;
1974: //ROGUE
1975:
1976: ent->waterlevel = pm.waterlevel;
1977: ent->watertype = pm.watertype;
1978: ent->groundentity = pm.groundentity;
1979: if (pm.groundentity)
1980: ent->groundentity_linkcount = pm.groundentity->linkcount;
1981:
1982: if (ent->deadflag)
1983: {
1984: client->ps.viewangles[ROLL] = 40;
1985: client->ps.viewangles[PITCH] = -15;
1986: client->ps.viewangles[YAW] = client->killer_yaw;
1987: }
1988: else
1989: {
1990: VectorCopy (pm.viewangles, client->v_angle);
1991: VectorCopy (pm.viewangles, client->ps.viewangles);
1992: }
1993:
1994: gi.linkentity (ent);
1995:
1996: //PGM trigger_gravity support
1997: ent->gravity = 1.0;
1998: //PGM
1999: if (ent->movetype != MOVETYPE_NOCLIP)
2000: G_TouchTriggers (ent);
2001:
2002: // touch other objects
2003: for (i=0 ; i<pm.numtouch ; i++)
2004: {
2005: other = pm.touchents[i];
2006: for (j=0 ; j<i ; j++)
2007: if (pm.touchents[j] == other)
2008: break;
2009: if (j != i)
2010: continue; // duplicated
2011: if (!other->touch)
2012: continue;
2013: other->touch (other, ent, NULL, NULL);
2014: }
2015: }
2016:
2017: client->oldbuttons = client->buttons;
2018: client->buttons = ucmd->buttons;
2019: client->latched_buttons |= client->buttons & ~client->oldbuttons;
2020:
2021: // save light level the player is standing on for
2022: // monster sighting AI
2023: ent->light_level = ucmd->lightlevel;
2024:
2025: // fire weapon from final position if needed
2026: if (client->latched_buttons & BUTTON_ATTACK)
2027: {
2028: if (client->resp.spectator)
2029: {
2030: client->latched_buttons = 0;
2031:
2032: if (client->chase_target)
2033: {
2034: client->chase_target = NULL;
2035: client->ps.pmove.pm_flags &= ~PMF_NO_PREDICTION;
2036: }
2037: else
2038: GetChaseTarget(ent);
2039: }
2040: else if (!client->weapon_thunk)
2041: {
2042: client->weapon_thunk = true;
2043: Think_Weapon (ent);
2044: }
2045: }
2046:
2047: if (client->resp.spectator)
2048: {
2049: if (ucmd->upmove >= 10)
2050: {
2051: if (!(client->ps.pmove.pm_flags & PMF_JUMP_HELD))
2052: {
2053: client->ps.pmove.pm_flags |= PMF_JUMP_HELD;
2054: if (client->chase_target)
2055: ChaseNext(ent);
2056: else
2057: GetChaseTarget(ent);
2058: }
2059: }
2060: else
2061: client->ps.pmove.pm_flags &= ~PMF_JUMP_HELD;
2062: }
2063:
2064: // update chase cam if being followed
2065: for (i = 1; i <= maxclients->value; i++)
2066: {
2067: other = g_edicts + i;
2068: if (other->inuse && other->client->chase_target == ent)
2069: UpdateChaseCam(other);
2070: }
2071: }
2072:
2073:
2074: /*
2075: ==============
2076: ClientBeginServerFrame
2077:
2078: This will be called once for each server frame, before running
2079: any other entities in the world.
2080: ==============
2081: */
2082: void ClientBeginServerFrame (edict_t *ent)
2083: {
2084: gclient_t *client;
2085: int buttonMask;
2086:
2087: if (level.intermissiontime)
2088: return;
2089:
2090: client = ent->client;
2091:
2092: if (deathmatch->value &&
2093: client->pers.spectator != client->resp.spectator &&
2094: (level.time - client->respawn_time) >= 5)
2095: {
2096: spectator_respawn(ent);
2097: return;
2098: }
2099:
2100: // run weapon animations if it hasn't been done by a ucmd_t
2101: if (!client->weapon_thunk && !client->resp.spectator)
2102: Think_Weapon (ent);
2103: else
2104: client->weapon_thunk = false;
2105:
2106: if (ent->deadflag)
2107: {
2108: // wait for any button just going down
2109: if ( level.time > client->respawn_time)
2110: {
2111: // in deathmatch, only wait for attack button
2112: if (deathmatch->value)
2113: buttonMask = BUTTON_ATTACK;
2114: else
2115: buttonMask = -1;
2116:
2117: if ( ( client->latched_buttons & buttonMask ) ||
2118: (deathmatch->value && ((int)dmflags->value & DF_FORCE_RESPAWN) ) )
2119: {
2120: respawn(ent);
2121: client->latched_buttons = 0;
2122: }
2123: }
2124: return;
2125: }
2126:
2127: // add player trail so monsters can follow
2128: if (!deathmatch->value)
2129: if (!visible (ent, PlayerTrail_LastSpot() ) )
2130: PlayerTrail_Add (ent->s.old_origin);
2131:
2132: client->latched_buttons = 0;
2133: }
2134:
2135: /*
2136: ==============
2137: RemoveAttackingPainDaemons
2138:
2139: This is called to clean up the pain daemons that the disruptor attaches
2140: to clients to damage them.
2141: ==============
2142: */
2143: void RemoveAttackingPainDaemons (edict_t *self)
2144: {
2145: edict_t *tracker;
2146:
2147: tracker = G_Find (NULL, FOFS(classname), "pain daemon");
2148: while(tracker)
2149: {
2150: if(tracker->enemy == self)
2151: G_FreeEdict(tracker);
2152: tracker = G_Find (tracker, FOFS(classname), "pain daemon");
2153: }
2154:
2155: if(self->client)
2156: self->client->tracker_pain_framenum = 0;
2157: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.