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