]> icculus.org git repositories - divverent/nexuiz.git/blob - data/qcsrc/server/gamec/cl_client.c
drop all runes when going to spectator mode
[divverent/nexuiz.git] / data / qcsrc / server / gamec / cl_client.c
1 void info_player_start (void)
2 {
3         self.classname = "info_player_deathmatch";
4         relocate_spawnpoint();
5 }
6
7 void info_player_deathmatch (void)
8 {
9         relocate_spawnpoint();
10 }
11
12 float spawn_allbad;
13 float spawn_allgood;
14 entity Spawn_FilterOutBadSpots(entity firstspot, entity playerlist, float mindist, float teamcheck)
15 {
16         local entity spot, player, nextspot, previousspot, newfirstspot;
17         local float pcount;
18         spot = firstspot;
19         newfirstspot = world;
20         previousspot = world;
21         spawn_allgood = TRUE;
22         spawn_allbad = TRUE;
23         while (spot)
24         {
25                 nextspot = spot.chain;
26                 // count team mismatches as bad spots
27                 if (spot.team == teamcheck)
28                 {
29                         pcount = 0;
30                         player = playerlist;
31                         while (player)
32                         {
33                                 if (player != self)
34                                 if (vlen(player.origin - spot.origin) < mindist)
35                                         pcount = pcount + 1;
36                                 player = player.chain;
37                         }
38                         if (!pcount)
39                         {
40                                 spawn_allbad = FALSE;
41                                 if (newfirstspot)
42                                         previousspot.chain = spot;
43                                 else
44                                         newfirstspot = spot;
45                                 previousspot = spot;
46                                 spot.chain = world;
47                         }
48                         else
49                                 spawn_allgood = FALSE;
50                 }
51                 spot = nextspot;
52         }
53         // if we couldn't find ANY good points, return the original list
54         if (!newfirstspot)
55                 newfirstspot = firstspot;
56         return newfirstspot;
57 }
58
59 entity Spawn_RandomPoint(entity firstspot)
60 {
61         local entity spot;
62         local float numspots;
63         // count number of spots
64         numspots = 0;
65         spot = firstspot;
66         while (spot)
67         {
68                 numspots = numspots + 1;
69                 spot = spot.chain;
70         }
71         // pick a random one
72         numspots = numspots * random();
73         spot = firstspot;
74         while (spot.chain && numspots >= 1)
75         {
76                 numspots = numspots - 1;
77                 spot = spot.chain;
78         }
79         return spot;
80 }
81
82 entity Spawn_FurthestPoint(entity firstspot, entity playerlist)
83 {
84         local entity best, spot, player;
85         local float bestrating, rating;
86         best = world;
87         bestrating = -1000000;
88         spot = firstspot;
89         while (spot)
90         {
91                 rating = 1000000000;
92                 player = playerlist;
93                 while (player)
94                 {
95                         if (player != self)
96                                 rating = min(rating, vlen(player.origin - spot.origin));
97                         player = player.chain;
98                 }
99                 rating = rating + random() * 16;
100                 if (bestrating < rating)
101                 {
102                         best = spot;
103                         bestrating = rating;
104                 }
105                 spot = spot.chain;
106         }
107         return best;
108 }
109
110 /*
111 =============
112 SelectSpawnPoint
113
114 Finds a point to respawn
115 =============
116 */
117 entity SelectSpawnPoint (float anypoint)
118 {
119         local float teamcheck;
120         local entity spot, firstspot, playerlist;
121         string spotname;
122
123         spot = find (world, classname, "testplayerstart");
124         if (spot)
125                 return spot;
126
127         spotname = "info_player_deathmatch";
128         teamcheck = 0;
129
130         if(!anypoint && cvar("g_ctf") )
131                 teamcheck = self.team;
132
133         // get the list of players
134         playerlist = findchain(classname, "player");
135         // get the entire list of spots
136         firstspot = findchain(classname, "info_player_deathmatch");
137         // filter out the bad ones
138         // (note this returns the original list if none survived)
139         firstspot = Spawn_FilterOutBadSpots(firstspot, playerlist, 100, teamcheck);
140
141         // there is 50/50 chance of choosing a random spot or the furthest spot
142         // (this means that roughly every other spawn will be furthest, so you
143         // usually won't get fragged at spawn twice in a row)
144         if (random() > 0.5 || spawn_allbad || spawn_allgood)
145                 spot = Spawn_RandomPoint(firstspot);
146         else
147                 spot = Spawn_FurthestPoint(firstspot, playerlist);
148
149         if (!spot)
150         {
151                 if(cvar("spawn_debug"))
152                         GotoNextMap();
153                 else
154                         error ("PutClientInServer: no start points on level");
155         }
156
157         return spot;
158 }
159
160 /*
161 =============
162 CheckPlayerModel
163
164 Checks if the argument string can be a valid playermodel.
165 Returns a valid one in doubt.
166 =============
167 */
168 string CheckPlayerModel(string plyermodel) {
169         if( substring(plyermodel,0,14) != "models/player/") plyermodel = "models/player/marine.zym";
170
171         /* Possible Fixme: Check if server can open the model?
172            This would kill custom models, however. */
173
174         return plyermodel;
175 }
176
177 /*
178 =============
179 PutObserverInServer
180
181 putting a client as observer in the server
182 =============
183 */
184 void PutObserverInServer (void)
185 {
186         entity  spot;
187         spot = SelectSpawnPoint (FALSE);
188         RemoveGrapplingHook(self); // Wazat's Grappling Hook
189
190         if (cvar("g_runematch"))
191                 DropAllRunes(self);
192
193         if(self.frags <= 0 && self.frags > -666 && cvar("g_lms") && self.killcount != -666)
194                 bprint (strcat("^4", self.netname, "^4 has no more lives left\n"));
195         else if(self.killcount != -666)
196                 bprint (strcat("^4", self.netname, "^4 is spectating now\n"));
197
198         self.classname = "observer";
199         self.health = -666;
200         self.takedamage = DAMAGE_NO;
201         self.solid = SOLID_NOT;
202         self.movetype = MOVETYPE_NOCLIP;
203         self.flags = FL_CLIENT | FL_NOTARGET;
204         self.armorvalue = 666;
205         self.effects = 0;
206         self.armorvalue = cvar("g_balance_armor_start");
207         self.pauserotarmor_finished = 0;
208         self.pauserothealth_finished = 0;
209         self.pauseregen_finished = 0;
210         self.damageforcescale = 0;
211         self.death_time = 0;
212         self.dead_time = 0;
213         self.dead_frame = 0;
214         self.die_frame = 0;
215         self.deaths = 0;
216         self.alpha = 0;
217         self.scale = 0;
218         self.fade_time = 0;
219         self.pain_frame = 0;
220         self.pain_finished = 0;
221         self.strength_finished = 0;
222         self.invincible_finished = 0;
223         self.pushltime = 0;
224         self.think = SUB_Null;
225         self.nextthink = 0;
226         self.hook_time = 0;
227         self.runes = 0;
228         self.deadflag = DEAD_NO;
229         self.angles = spot.angles;
230         self.angles_z = 0;
231         self.fixangle = TRUE;
232         self.crouch = FALSE;
233         self.view_ofs = PL_VIEW_OFS;
234         setorigin (self, spot.origin + '0 0 1' * (1 - self.mins_z - 14));
235         self.oldorigin = self.origin;
236         self.items = 0;
237         self.model = "";
238         self.modelindex = 0;
239         self.weapon = 0;
240         self.weaponmodel = "";
241         self.weaponframe = 0;
242         self.weaponentity = world;
243         self.killcount = -666;
244         self.velocity = '0 0 0';
245         self.avelocity = '0 0 0';
246         self.punchangle = '0 0 0';
247         self.punchvector = '0 0 0';
248         self.oldvelocity = self.velocity;
249
250         if(!cvar("g_lms"))
251                 self.frags = -666;
252         //stuffcmd(self, "set viewsize 120 \n");
253 //      bprint (strcat("^4", self.netname, "^4 is spectating now\n"));
254 }
255
256
257 /*
258 =============
259 PutClientInServer
260
261 Called when a client spawns in the server
262 =============
263 */
264 void PutClientInServer (void)
265 {
266         if(clienttype(self) ==  CLIENTTYPE_BOT)
267         {
268                 self.classname = "player";
269         }
270
271         // player is dead and becomes observer
272         if(cvar("g_lms") && self.frags < 1)
273                 self.classname = "observer";
274
275         if(self.classname == "player") {
276                 entity  spot;
277
278                 spot = SelectSpawnPoint (FALSE);
279
280                 RemoveGrapplingHook(self); // Wazat's Grappling Hook
281
282                 self.classname = "player";
283                 self.iscreature = TRUE;
284                 self.movetype = MOVETYPE_WALK;
285                 self.solid = SOLID_SLIDEBOX;
286                 self.flags = FL_CLIENT;
287                 self.takedamage = DAMAGE_AIM;
288                 self.effects = 0;
289                 self.health = cvar("g_balance_health_start");
290                 self.armorvalue = cvar("g_balance_armor_start");
291                 self.spawnshieldtime = time + cvar("g_spawnshieldtime");
292                 self.pauserotarmor_finished = time + cvar("g_balance_pause_armor_rot_spawn");
293                 self.pauserothealth_finished = time + cvar("g_balance_pause_health_rot_spawn");
294                 self.pauseregen_finished = time + cvar("g_balance_pause_health_regen_spawn");
295                 self.damageforcescale = 2;
296                 self.death_time = 0;
297                 self.dead_time = 0;
298                 self.dead_frame = 0;
299                 self.die_frame = 0;
300                 self.alpha = 0;
301                 self.scale = 0;
302                 self.fade_time = 0;
303                 self.pain_frame = 0;
304                 self.pain_finished = 0;
305                 self.strength_finished = 0;
306                 self.invincible_finished = 0;
307                 self.pushltime = 0;
308                 //self.speed_finished = 0;
309                 //self.slowmo_finished = 0;
310                 // players have no think function
311                 self.think = SUB_Null;
312                 self.nextthink = 0;
313                 self.weapon = 0;
314                 self.switchweapon = 0;
315                 self.hook_time = 0;
316
317                 self.runes = 0;
318
319                 self.deadflag = DEAD_NO;
320
321                 self.angles = spot.angles;
322
323                 self.angles_z = 0; // never spawn tilted even if the spot says to
324                 self.fixangle = TRUE; // turn this way immediately
325                 self.velocity = '0 0 0';
326                 self.avelocity = '0 0 0';
327                 self.punchangle = '0 0 0';
328                 self.punchvector = '0 0 0';
329                 self.oldvelocity = self.velocity;
330
331                 self.viewzoom = 0.6;
332
333                 if(cvar("sv_defaultcharacter") == 1) {
334                         local string defaultmodel;
335                         defaultmodel = CheckPlayerModel(cvar_string("sv_defaultplayermodel"));
336
337                         precache_model (defaultmodel);
338                         setmodel (self, defaultmodel);
339                         self.skin = stof(cvar_string("sv_defaultplayerskin"));
340                 } else {
341                         self.playermodel = CheckPlayerModel(self.playermodel);
342
343                         precache_model (self.playermodel);
344                         setmodel (self, self.playermodel);
345                         self.skin = stof(self.playerskin);
346
347                 }
348
349                 self.crouch = FALSE;
350                 self.view_ofs = PL_VIEW_OFS;
351                 setsize (self, PL_MIN, PL_MAX);
352                 setorigin (self, spot.origin + '0 0 1' * (1 - self.mins_z - 24));
353                 // don't reset back to last position, even if new position is stuck in solid
354                 self.oldorigin = self.origin;
355
356                 if(cvar("g_lms"))
357                 {
358                         self.ammo_shells = cvar("g_lms_start_ammo_shells");
359                         self.ammo_nails = cvar("g_lms_start_ammo_nails");
360                         self.ammo_rockets = cvar("g_lms_start_ammo_rockets");
361                         self.ammo_cells = cvar("g_lms_start_ammo_cells");
362                         self.health = cvar("g_lms_start_health");
363                         self.armorvalue = cvar("g_lms_start_armor");
364                 }
365                 else if (cvar("g_use_ammunition")) {
366                         self.ammo_shells = cvar("g_start_ammo_shells");
367                         self.ammo_nails = cvar("g_start_ammo_nails");
368                         self.ammo_rockets = cvar("g_start_ammo_rockets");
369                         self.ammo_cells = cvar("g_start_ammo_cells");
370                 } else {
371                         self.ammo_shells = 999;
372                         self.ammo_nails = 999;
373                         self.ammo_rockets = 999;
374                         self.ammo_cells = 999;
375                 }
376
377                 self.items = 0;
378                 if (cvar("g_start_weapon_laser") || cvar("g_lms"))
379                 {
380                         self.items = self.items | IT_LASER;
381                         self.switchweapon = WEP_LASER;
382                 }
383                 if (cvar("g_start_weapon_shotgun") || cvar("g_lms"))
384                 {
385                         self.items = self.items | IT_SHOTGUN;
386                         self.switchweapon = WEP_SHOTGUN;
387                 }
388                 if (cvar("g_start_weapon_uzi") || cvar("g_lms"))
389                 {
390                         self.items = self.items | IT_UZI;
391                         self.switchweapon = WEP_UZI;
392                 }
393                 if (cvar("g_start_weapon_grenadelauncher") || cvar("g_lms"))
394                 {
395                         self.items = self.items | IT_GRENADE_LAUNCHER;
396                         self.switchweapon = WEP_GRENADE_LAUNCHER;
397                 }
398                 if (cvar("g_start_weapon_electro") || cvar("g_lms"))
399                 {
400                         self.items = self.items | IT_ELECTRO;
401                         self.switchweapon = WEP_ELECTRO;
402                 }
403                 if (cvar("g_start_weapon_crylink") || cvar("g_lms"))
404                 {
405                         self.items = self.items | IT_CRYLINK;
406                         self.switchweapon = WEP_CRYLINK;
407                 }
408                 if (cvar("g_start_weapon_nex") || cvar("g_lms"))
409                 {
410                         self.items = self.items | IT_NEX;
411                         self.switchweapon = WEP_NEX;
412                 }
413                 if (cvar("g_start_weapon_hagar") || cvar("g_lms"))
414                 {
415                         self.items = self.items | IT_HAGAR;
416                         self.switchweapon = WEP_HAGAR;
417                 }
418                 if (cvar("g_start_weapon_rocketlauncher") || cvar("g_lms"))
419                 {
420                         self.items = self.items | IT_ROCKET_LAUNCHER;
421                         self.switchweapon = WEP_ROCKET_LAUNCHER;
422                 }
423
424                 if(cvar("g_instagib"))
425                 {
426                         self.items = IT_NEX;
427                         self.switchweapon = WEP_NEX;
428                         self.ammo_cells = 999;
429                 }
430
431                 if(cvar("g_rocketarena"))
432                 {
433                         self.items = IT_ROCKET_LAUNCHER;
434                         self.switchweapon = WEP_ROCKET_LAUNCHER;
435                         self.ammo_rockets = 999;
436                 }
437
438                 if(cvar("g_nixnex"))
439                 {
440                         self.items = 0;
441                         // will be done later
442                 }
443
444                 if(cvar("g_minstagib"))
445                 {
446                         self.health = 100;
447                         self.armorvalue = 0;
448                         self.items = IT_NEX;
449                         self.switchweapon = WEP_NEX;
450                         self.ammo_cells = cvar("g_minstagib_ammo_start");
451                         self.extralives = 0;
452                         self.jump_interval = time;
453                 }
454
455                 self.event_damage = PlayerDamage;
456
457                 self.statdraintime = time + 5;
458                 self.button0 = self.button1 = self.button2 = self.button3 = 0;
459
460                 if(self.killcount == -666) {
461                         self.killcount = 0;
462                         self.frags = 0;
463                 }
464
465                 self.cnt = WEP_LASER;
466                 self.nixnex_lastchange_id = -1;
467
468                 /*
469                 W_UpdateWeapon();
470                 W_UpdateAmmo();
471                 */
472                 CL_SpawnWeaponentity();
473                 self.alpha = 1;
474                 self.exteriorweaponentity.alpha = 1;
475
476                 self.lms_nextcheck = time + cvar("g_lms_campcheck_interval")*2;
477                 self.lms_traveled_distance = 0;
478
479                 if(cvar("spawn_debug"))
480                 {
481                                 sprint(self, strcat("spawnpoint origin:  ", vtos(spot.origin), "\n"));
482                                 remove(spot);   // usefull for checking if there are spawnpoints, that let drop through the floor
483                 }
484
485                 //stuffcmd(self, "chase_active 0");
486                 //stuffcmd(self, "set viewsize $tmpviewsize \n");
487         } else if(self.classname == "observer") {
488                 PutObserverInServer ();
489         }
490 }
491
492 /*
493 =============
494 SetNewParms
495 =============
496 */
497 void SetNewParms (void)
498 {
499
500 }
501
502 /*
503 =============
504 SetChangeParms
505 =============
506 */
507 void SetChangeParms (void)
508 {
509
510 }
511
512 /*
513 =============
514 ClientKill
515
516 Called when a client types 'kill' in the console
517 =============
518 */
519 void ClientKill (void)
520 {
521         Damage(self, self, self, 100000, DEATH_KILL, self.origin, '0 0 0');
522 }
523
524 /*
525 =============
526 ClientConnect
527
528 Called when a client connects to the server
529 =============
530 */
531 string ColoredTeamName(float t);
532 //void dom_player_join_team(entity pl);
533 void ClientConnect (void)
534 {
535         self.classname = "player_joining";
536
537         if(player_count<0) player_count = 0;
538
539         //if(cvar("g_domination"))
540         //      dom_player_join_team(self);
541
542         //JoinBestTeam(self, FALSE);
543
544         if(cvar("sv_spectate") == 1 && !cvar("g_lms")) {
545                 self.classname = "observer";
546         } else {
547                 self.classname = "player";
548         }
549
550         self.playerid = (playerid_last = playerid_last + 1);
551         if(cvar("sv_eventlog"))
552         {
553                 string s;
554                 if(clienttype(self) == CLIENTTYPE_REAL)
555                         s = "player";
556                 else
557                         s = "bot";
558                 GameLogEcho(strcat(":join:", ftos(self.playerid), ":", s, ":", self.netname), TRUE);
559                 s = strcat(":team:", ftos(self.playerid), ":");
560                 s = strcat(s, ftos(self.team));
561                 GameLogEcho(s, FALSE);
562         }
563
564         //stuffcmd(self, "set tmpviewsize $viewsize \n");
565
566         bprint ("^4",self.netname);
567         bprint ("^4 connected");
568
569         if(cvar("g_domination") || cvar("g_ctf"))
570         {
571                 bprint(" and joined the ");
572                 bprint(ColoredTeamName(self.team));
573         }
574
575         bprint("\n");
576
577         self.welcomemessage_time = time + cvar("welcome_message_time");
578         self.welcomemessage_time2 = 0;
579
580         stuffcmd(self, strcat("exec maps/", mapname, ".cfg\n"));
581         // send prediction settings to the client
582         stuffcmd(self, strcat("cl_movement_maxspeed ", ftos(cvar("sv_maxspeed")), "\n"));
583         stuffcmd(self, strcat("cl_movement_maxairspeed ", ftos(cvar("sv_maxairspeed")), "\n"));
584         stuffcmd(self, strcat("cl_movement_accelerate ", ftos(cvar("sv_accelerate")), "\n"));
585         stuffcmd(self, strcat("cl_movement_friction ", ftos(cvar("sv_friction")), "\n"));
586         stuffcmd(self, strcat("cl_movement_stopspeed ", ftos(cvar("sv_stopspeed")), "\n"));
587         stuffcmd(self, strcat("cl_movement_jumpvelocity ", ftos(cvar("g_balance_jumpheight")), "\n"));
588         stuffcmd(self, strcat("cl_movement_stepheight ", ftos(cvar("sv_stepheight")), "\n"));
589         stuffcmd(self, strcat("set cl_movement_friction_on_land ", ftos(cvar("sv_friction_on_land")), "\n"));
590         stuffcmd(self, strcat("cl_movement_edgefriction 0\n"));
591         // Wazat's grappling hook
592         SetGrappleHookBindings();
593
594         // get autoswitch state from player
595         stuffcmd(self, "alias autoswitch \"set cl_autoswitch $1; cmd autoswitch $1\"\n");
596         stuffcmd(self, "cmd autoswitch $cl_autoswitch\n");
597
598         // get version info from player
599         stuffcmd(self, "cmd clientversion $gameversion\n");
600
601         // set cvar for team scoreboard
602         stuffcmd(self, strcat("set teamplay ", ftos(teams_matter), "\n"));
603
604         if(cvar("g_lms"))
605         {
606                 self.frags = cvar("fraglimit");
607                 // no fraglimit was set, so player gets 999 lives
608                 if(self.frags < 1)
609                         self.frags = 999;
610
611                 // disallow player to join after the worst player has lost g_lms_last_join lives
612                 // if "g_lms_join_anytime" new players spawn with same amount of lives as the worst active player
613                 if(((cvar("fraglimit") - cvar("g_lms_last_join")) > lms_lowest_lives && !cvar("g_lms_join_anytime")) || lms_lowest_lives < 1)
614                 {
615                         self.frags = -666;
616                         lms_dead_count += 1;
617                 }
618                 else if(cvar("fraglimit") > lms_lowest_lives)
619                 {
620                         self.frags = lms_lowest_lives;
621                 }
622         }
623
624         player_count += 1;
625         self.jointime = time;
626 }
627
628 /*
629 =============
630 ClientDisconnect
631
632 Called when a client disconnects from the server
633 =============
634 */
635 void(entity e) DropFlag;
636 .entity chatbubbleentity;
637 .entity teambubbleentity;
638 void ClientDisconnect (void)
639 {
640         if(cvar("sv_eventlog"))
641                 GameLogEcho(strcat(":part:", ftos(self.playerid)), FALSE);
642         bprint ("^4",self.netname);
643         bprint ("^4 disconnected\n");
644
645         if (self.chatbubbleentity)
646         {
647                 remove (self.chatbubbleentity);
648                 self.chatbubbleentity = world;
649         }
650
651         if (self.teambubbleentity)
652         {
653                 remove (self.teambubbleentity);
654                 self.teambubbleentity = world;
655         }
656
657         DropAllRunes(self);
658
659         if(self.flagcarried)
660                 DropFlag(self.flagcarried);
661
662         // decrease player count for lms
663         player_count -= 1;
664         // player was dead, decrease dead count
665         if(cvar("g_lms") && self.frags < 1)
666                 lms_dead_count -= 1;
667         //stuffcmd(self, "set viewsize $tmpviewsize \n");
668 }
669
670 .float buttonchat;
671 void() ChatBubbleThink =
672 {
673         self.nextthink = time;
674         if (!self.owner.modelindex || self.owner.chatbubbleentity != self)
675         {
676                 remove(self);
677                 return;
678         }
679         setorigin(self, self.owner.origin + '0 0 15' + self.owner.maxs_z * '0 0 1');
680         if (self.owner.buttonchat && !self.owner.deadflag)
681                 self.model = self.mdl;
682         else
683                 self.model = "";
684 };
685
686 void() UpdateChatBubble =
687 {
688         if (!self.modelindex)
689                 return;
690         // spawn a chatbubble entity if needed
691         if (!self.chatbubbleentity)
692         {
693                 self.chatbubbleentity = spawn();
694                 self.chatbubbleentity.owner = self;
695                 self.chatbubbleentity.exteriormodeltoclient = self;
696                 self.chatbubbleentity.think = ChatBubbleThink;
697                 self.chatbubbleentity.nextthink = time;
698                 setmodel(self.chatbubbleentity, "models/misc/chatbubble.spr");
699                 setorigin(self.chatbubbleentity, self.origin + '0 0 15' + self.maxs_z * '0 0 1');
700                 self.chatbubbleentity.mdl = self.chatbubbleentity.model;
701                 self.chatbubbleentity.model = "";
702         }
703 }
704
705
706 void() TeamBubbleThink =
707 {
708         self.nextthink = time;
709         if (!self.owner.modelindex || self.owner.teambubbleentity != self)
710         {
711                 remove(self);
712                 return;
713         }
714 //      setorigin(self, self.owner.origin + '0 0 15' + self.owner.maxs_z * '0 0 1');  // bandwidth hog. setattachment does this now
715         if (self.owner.buttonchat || self.owner.deadflag)
716                 self.model = "";
717         else
718                 self.model = self.mdl;
719
720 };
721
722 .float() customizeentityforclient;
723 float() ChatBubble_customizeentityforclient = {return (self.owner.team == other.team && other.killcount > -666);};
724
725 void() UpdateTeamBubble =
726 {
727         if (!self.modelindex || !cvar("teamplay"))
728                 return;
729         // spawn a teambubble entity if needed
730         if (!self.teambubbleentity && cvar("teamplay"))
731         {
732                 self.teambubbleentity = spawn();
733                 self.teambubbleentity.owner = self;
734                 self.teambubbleentity.exteriormodeltoclient = self;
735                 self.teambubbleentity.think = TeamBubbleThink;
736                 self.teambubbleentity.nextthink = time;
737                 setmodel(self.teambubbleentity, "models/misc/teambubble.spr");
738 //              setorigin(self.teambubbleentity, self.origin + '0 0 15' + self.maxs_z * '0 0 1');
739                 setorigin(self.teambubbleentity, self.teambubbleentity.origin + '0 0 15' + self.maxs_z * '0 0 1');
740                 setattachment(self.teambubbleentity, self, "");  // sticks to moving player better, also conserves bandwidth
741                 self.teambubbleentity.mdl = self.teambubbleentity.model;
742                 self.teambubbleentity.model = self.teambubbleentity.mdl;
743                 self.teambubbleentity.customizeentityforclient = ChatBubble_customizeentityforclient;
744         }
745 }
746
747 // LordHavoc: this hack will be removed when proper _pants/_shirt layers are
748 // added to the model skins
749 /*void() UpdateColorModHack =
750 {
751         local float c;
752         c = self.clientcolors & 15;
753         // LordHavoc: only bothering to support white, green, red, yellow, blue
754              if (teamplay == 0) self.colormod = '0 0 0';
755         else if (c ==  0) self.colormod = '1.00 1.00 1.00';
756         else if (c ==  3) self.colormod = '0.10 1.73 0.10';
757         else if (c ==  4) self.colormod = '1.73 0.10 0.10';
758         else if (c == 12) self.colormod = '1.22 1.22 0.10';
759         else if (c == 13) self.colormod = '0.10 0.10 1.73';
760         else self.colormod = '1 1 1';
761 };*/
762
763 void UpdatePlayerColors () {
764         if(self.weaponentity) {
765                 self.weaponentity.colormap = self.colormap;
766                 self.exteriorweaponentity.colormap = self.colormap;
767         }
768 }
769 /*
770 =============
771 PlayerJump
772
773 When you press the jump key
774 =============
775 */
776 void PlayerJump (void)
777 {
778         float mjumpheight;
779
780         mjumpheight = cvar("g_balance_jumpheight");
781         if (self.waterlevel >= 2)
782         {
783                 if (self.watertype == CONTENT_WATER)
784                         self.velocity_z = 200;
785                 else if (self.watertype == CONTENT_SLIME)
786                         self.velocity_z = 80;
787                 else
788                         self.velocity_z = 50;
789
790                 return;
791         }
792
793
794         if (!(self.flags & FL_ONGROUND))
795                 return;
796
797         if (!(self.flags & FL_JUMPRELEASED))
798                 return;
799
800         if(cvar("g_runematch"))
801         {
802                 if(self.runes & RUNE_SPEED)
803                 {
804                         if(self.runes & CURSE_SLOW)
805                                 mjumpheight = mjumpheight * cvar("g_balance_rune_speed_combo_jumpheight");
806                         else
807                                 mjumpheight = mjumpheight * cvar("g_balance_rune_speed_jumpheight");
808                 }
809                 else if(self.runes & CURSE_SLOW)
810                 {
811                         mjumpheight = mjumpheight * cvar("g_balance_curse_slow_jumpheight");
812                 }
813         }
814
815         if(cvar("g_minstagib") && (self.items & IT_INVINCIBLE))
816         {
817                 mjumpheight = mjumpheight * cvar("g_balance_rune_speed_jumpheight");
818         }
819
820         self.velocity_z = self.velocity_z + mjumpheight;
821         self.oldvelocity_z = self.velocity_z;
822
823         self.flags = self.flags - FL_ONGROUND;
824         self.flags = self.flags - FL_JUMPRELEASED;
825 }
826
827 void() CheckWaterJump =
828 {
829         local vector start, end;
830
831 // check for a jump-out-of-water
832         makevectors (self.angles);
833         start = self.origin;
834         start_z = start_z + 8;
835         v_forward_z = 0;
836         normalize(v_forward);
837         end = start + v_forward*24;
838         traceline (start, end, TRUE, self);
839         if (trace_fraction < 1)
840         {       // solid at waist
841                 start_z = start_z + self.maxs_z - 8;
842                 end = start + v_forward*24;
843                 self.movedir = trace_plane_normal * -50;
844                 traceline (start, end, TRUE, self);
845                 if (trace_fraction == 1)
846                 {       // open at eye level
847                         self.flags = self.flags | FL_WATERJUMP;
848                         self.velocity_z = 225;
849                         self.flags = self.flags - (self.flags & FL_JUMPRELEASED);
850                         self.teleport_time = time + 2;  // safety net
851                         return;
852                 }
853         }
854 };
855
856
857 void respawn(void)
858 {
859         CopyBody(1);
860         PutClientInServer();
861 }
862
863 void player_powerups (void)
864 {
865         if (cvar("g_minstagib"))
866         {
867                 self.effects = EF_FULLBRIGHT;
868                 if (self.items & IT_STRENGTH)
869                 {
870                         if (time > self.strength_finished)
871                         {
872                                 self.alpha = 1;
873                                 self.exteriorweaponentity.alpha = 1;
874                                 self.items = self.items - (self.items & IT_STRENGTH);
875                                 sprint(self, "^3Invisibility has worn off\n");
876                         }
877                 }
878                 else
879                 {
880                         if (time < self.strength_finished)
881                         {
882                                 self.alpha = cvar("g_minstagib_invis_alpha");
883                                 self.exteriorweaponentity.alpha = cvar("g_minstagib_invis_alpha");
884                                 self.items = self.items | IT_STRENGTH;
885                                 sprint(self, "^3You are invisible\n");
886                         }
887                 }
888
889                 if (self.items & IT_INVINCIBLE)
890                 {
891                         if (time > self.invincible_finished)
892                         {
893                                 self.items = self.items - (self.items & IT_INVINCIBLE);
894                                 sprint(self, "^3Speed has worn off\n");
895                         }
896                 }
897                 else
898                 {
899                         if (time < self.invincible_finished)
900                         {
901                                 self.items = self.items | IT_INVINCIBLE;
902                                 sprint(self, "^3You are on speed\n");
903                         }
904                 }
905                 return;
906         }
907
908         self.effects = self.effects - (self.effects & (EF_RED | EF_BLUE | EF_ADDITIVE | EF_FULLBRIGHT));
909         if (self.items & IT_STRENGTH)
910         {
911                 self.effects = self.effects | (EF_BLUE | EF_ADDITIVE | EF_FULLBRIGHT);
912                 if (time > self.strength_finished)
913                 {
914                         self.items = self.items - (self.items & IT_STRENGTH);
915                         sprint(self, "^3Strength has worn off\n");
916                 }
917         }
918         else
919         {
920                 if (time < self.strength_finished)
921                 {
922                         self.items = self.items | IT_STRENGTH;
923                         sprint(self, "^3Strength infuses your weapons with devestating power\n");
924                 }
925         }
926         if (self.items & IT_INVINCIBLE)
927         {
928                 self.effects = self.effects | (EF_RED | EF_ADDITIVE | EF_FULLBRIGHT);
929                 if (time > self.invincible_finished)
930                 {
931                         self.items = self.items - (self.items & IT_INVINCIBLE);
932                         sprint(self, "^3Shield has worn off\n");
933                 }
934         }
935         else
936         {
937                 if (time < self.invincible_finished)
938                 {
939                         self.items = self.items | IT_INVINCIBLE;
940                         sprint(self, "^3Shield surrounds you\n");
941                 }
942         }
943
944         if (cvar("g_fullbrightplayers"))
945                 self.effects = self.effects | EF_FULLBRIGHT;
946
947         // midair gamemode: damage only while in the air
948         // if in midair mode, being on ground grants temporary invulnerability
949         // (this is so that multishot weapon don't clear the ground flag on the
950         // first damage in the frame, leaving the player vulnerable to the
951         // remaining hits in the same frame)
952         if (self.flags & FL_ONGROUND)
953         if (cvar("g_midair"))
954                 self.spawnshieldtime = max(self.spawnshieldtime, time + cvar("g_midair_shieldtime"));
955
956         if (time < self.spawnshieldtime)
957                 self.effects = self.effects | (EF_ADDITIVE | EF_FULLBRIGHT);
958 }
959
960 void player_regen (void)
961 {
962         float maxh, maxa, limith, limita, max_mod, regen_mod, rot_mod, limit_mod;
963         maxh = cvar("g_balance_health_stable");
964         maxa = cvar("g_balance_armor_stable");
965         limith = cvar("g_balance_health_limit");
966         limita = cvar("g_balance_armor_limit");
967
968         if (cvar("g_minstagib") || (cvar("g_lms") && !cvar("g_lms_regenerate")))
969                 return;
970
971         if(cvar("g_runematch"))
972         {
973                 max_mod = regen_mod = rot_mod = limit_mod = 1;
974                 if (self.runes & RUNE_REGEN)
975                 {
976                         if (self.runes & CURSE_VENOM) // do we have both rune/curse?
977                         {
978                                 regen_mod = cvar("g_balance_rune_regen_combo_regenrate");
979                                 max_mod = cvar("g_balance_rune_regen_combo_hpmod");
980                                 limit_mod = cvar("g_balance_rune_regen_combo_limitmod");
981                         }
982                         else
983                         {
984                                 regen_mod = cvar("g_balance_rune_regen_regenrate");
985                                 max_mod = cvar("g_balance_rune_regen_hpmod");
986                                 limit_mod = cvar("g_balance_rune_regen_limitmod");
987                         }
988                 }
989                 else if (self.runes & CURSE_VENOM)
990                 {
991                         max_mod = cvar("g_balance_curse_venom_hpmod");
992                         if (self.runes & RUNE_REGEN) // do we have both rune/curse?
993                                 rot_mod = cvar("g_balance_rune_regen_combo_rotrate");
994                         else
995                                 rot_mod = cvar("g_balance_curse_venom_rotrate");
996                         limit_mod = cvar("g_balance_curse_venom_limitmod");
997                         //if (!self.runes & RUNE_REGEN)
998                         //      rot_mod = cvar("g_balance_curse_venom_rotrate");
999                 }
1000                 maxh = maxh * max_mod;
1001                 //maxa = maxa * max_mod;
1002
1003                 if (time > self.pauserotarmor_finished)
1004                 {
1005                         if (self.armorvalue > maxa)
1006                                 self.armorvalue = bound(0, self.armorvalue + (maxa - self.armorvalue) * cvar("g_balance_armor_rot") * frametime, 1000);
1007                 }
1008                 if (time > self.pauserothealth_finished)
1009                 {
1010                         if (self.health > maxh)
1011                                 self.health = bound(0, self.health + (maxh - self.health) * rot_mod*cvar("g_balance_health_rot") * frametime, 1000);
1012                 }
1013                 if (time > self.pauseregen_finished)
1014                 {
1015                         if (self.health < maxh)
1016                                 self.health = bound(0, self.health + (maxh- self.health) * regen_mod*cvar("g_balance_health_regen") * frametime, 1000);
1017                         if (self.armorvalue < maxa)
1018                                 self.armorvalue = bound(0, self.armorvalue + (maxa - self.armorvalue) * cvar("g_balance_armor_regen") * frametime, 1000);
1019                 }
1020         }
1021         else
1022         {
1023                 if (time > self.pauserothealth_finished)
1024                 if (self.health > maxh)
1025                         self.health = bound(0, self.health + (maxh - self.health) * cvar("g_balance_health_rot") * frametime, 1000);
1026                 if (time > self.pauserotarmor_finished)
1027                 if (self.armorvalue > maxa)
1028                         self.armorvalue = bound(0, self.armorvalue + (maxa - self.armorvalue) * cvar("g_balance_armor_rot") * frametime, 1000);
1029                 if (time > self.pauseregen_finished)
1030                 {
1031                         if (self.health < maxh)
1032                                  self.health = bound(0, self.health + (maxh - self.health) * cvar("g_balance_health_regen") * frametime + 0.001, 1000);
1033                         if (self.armorvalue < maxa)
1034                                 self.armorvalue = bound(0, self.armorvalue + (maxa - self.armorvalue) * cvar("g_balance_armor_regen") * frametime + 0.001, 1000);
1035                 }
1036         }
1037         if (self.health > limith)
1038                 self.health = limith;
1039         if (self.armorvalue > limita)
1040                 self.armorvalue = limita;
1041 }
1042
1043 /*
1044 ======================
1045 spectate mode routines
1046 ======================
1047 */
1048 void SpectateCopy(entity spectatee) {
1049         self.armortype = spectatee.armortype;
1050         self.armorvalue = spectatee.armorvalue;
1051         self.currentammo = spectatee.currentammo;
1052         self.effects = spectatee.effects;
1053         self.health = spectatee.health;
1054         self.impulse = 0;
1055         self.items = spectatee.items;
1056         self.punchangle = spectatee.punchangle;
1057         self.view_ofs = spectatee.view_ofs;
1058         self.v_angle = spectatee.v_angle;
1059         self.viewzoom = spectatee.viewzoom;
1060         setorigin(self, spectatee.origin);
1061         setsize(self, spectatee.mins, spectatee.maxs);
1062 }
1063
1064 void SpectateUpdate() {
1065         if (self != self.enemy) {
1066                 SpectateCopy(self.enemy);
1067                 msg_entity = self;
1068                 WriteByte(MSG_ONE, SVC_SETANGLE);
1069                 WriteAngle(MSG_ONE, self.enemy.v_angle_x);
1070                 WriteAngle(MSG_ONE, self.enemy.v_angle_y);
1071                 WriteAngle(MSG_ONE, self.enemy.v_angle_z);
1072         }
1073 }
1074
1075 float SpectateNext() {
1076         other = find(self.enemy, classname, "player");
1077         if (!other) {
1078                 other = find(other, classname, "player");
1079         }
1080         if (other) {
1081                 self.enemy = other;
1082         }
1083         if(self.enemy.classname == "player") {
1084                 msg_entity = self;
1085                 WriteByte(MSG_ONE, SVC_SETVIEW);
1086                 WriteEntity(MSG_ONE, self.enemy);
1087                 //stuffcmd(self, "set viewsize $tmpviewsize \n");
1088                 SpectateUpdate();
1089                 return 1;
1090         } else {
1091                 return 0;
1092         }
1093 }
1094
1095 /*
1096 =============
1097 PlayerPreThink
1098
1099 Called every frame for each client before the physics are run
1100 =============
1101 */
1102 void PlayerPreThink (void)
1103 {
1104         if(self.classname == "player") {
1105                 local vector m1, m2;
1106
1107 //              MauveBot_AI();
1108
1109 //              if(self.netname == "Wazat")
1110 //                      bprint(strcat(self.classname, "\n"));
1111
1112                 CheckRules_Player();
1113
1114                 if(self.button7)
1115                         PrintWelcomeMessage(self);
1116
1117                 if(cvar("g_lms") || !cvar("sv_spectate"))
1118                 if((time - self.jointime) <= cvar("welcome_message_time"))
1119                         PrintWelcomeMessage(self);
1120
1121                 if (intermission_running)
1122                 {
1123                         IntermissionThink ();   // otherwise a button could be missed between
1124                         return;                                 // the think tics
1125                 }
1126
1127                 if (self.deadflag != DEAD_NO)
1128                 {
1129                         player_anim();
1130                         weapon_freeze();
1131                         if (self.deadflag == DEAD_DYING)
1132                         {
1133                                 if (time > self.dead_time)
1134                                         self.deadflag = DEAD_DEAD;
1135                         }
1136                         else if (self.deadflag == DEAD_DEAD)
1137                         {
1138                                 if (cvar("g_lms") || cvar("g_forced_respawn"))
1139                                         self.button0 = self.button2 = self.button3 = 0;
1140
1141                                 if (!self.button0 && !self.button2 && !self.button3)
1142                                         self.deadflag = DEAD_RESPAWNABLE;
1143                         }
1144                         else if (self.deadflag == DEAD_RESPAWNABLE)
1145                         {
1146                                 if (self.button0  ||
1147                                     self.button2  ||
1148                                     self.button3  ||
1149                                     self.button4  ||
1150                                     cvar("g_lms") ||
1151                                     cvar("g_forced_respawn"))
1152                                         respawn();
1153                         }
1154                         return;
1155                 }
1156
1157                 if(cvar("g_lms") && !self.deadflag && cvar("g_lms_campcheck_interval"))
1158                 {
1159                         vector dist;
1160
1161                         // calculate player movement (in 2 dimensions only, so jumping on one spot doesn't count as movement)
1162                         dist = self.oldorigin - self.origin;
1163                         dist_z = 0;
1164                         self.lms_traveled_distance += fabs(vlen(dist));
1165
1166                         if(time > self.lms_nextcheck)
1167                         {
1168                                 //sprint(self, "distance: ", ftos(self.lms_traveled_distance), "\n");
1169                                 if(self.lms_traveled_distance < cvar("g_lms_campcheck_distance"))
1170                                 {
1171                                         centerprint(self, cvar_string("g_lms_campcheck_message"));
1172                                         // FIXME KadaverJack: gibbing player here causes playermodel to bounce around, instead of eye.md3
1173                                         // I wasn't able to find out WHY that happens, so I put a workaround in place that shall prevent players from being gibbed :(
1174                                         Damage(self, self, self, bound(0, cvar("g_lms_campcheck_damage"), self.health + self.armorvalue * cvar("g_balance_armor_blockpercent") + 5), DEATH_CAMP, self.origin, '0 0 0');
1175                                 }
1176                                 self.lms_nextcheck = time + cvar("g_lms_campcheck_interval");
1177                                 self.lms_traveled_distance = 0;
1178                         }
1179                 }
1180
1181                 if (self.button5)
1182                 {
1183                         if (!self.crouch)
1184                         {
1185                                 self.crouch = TRUE;
1186                                 self.view_ofs = PL_CROUCH_VIEW_OFS;
1187                                 setsize (self, PL_CROUCH_MIN, PL_CROUCH_MAX);
1188                         }
1189                 }
1190                 else
1191                 {
1192                         if (self.crouch)
1193                         {
1194                                 tracebox(self.origin, PL_MIN, PL_MAX, self.origin, FALSE, self);
1195                                 if (!trace_startsolid)
1196                                 {
1197                                         self.crouch = FALSE;
1198                                         self.view_ofs = PL_VIEW_OFS;
1199                                         setsize (self, PL_MIN, PL_MAX);
1200                                 }
1201                         }
1202                 }
1203
1204                 if(cvar("sv_defaultcharacter") == 1) {
1205                         local string defaultmodel;
1206                         defaultmodel = CheckPlayerModel(cvar_string("sv_defaultplayermodel"));
1207
1208                         if (defaultmodel != self.model)
1209                         {
1210                                 m1 = self.mins;
1211                                 m2 = self.maxs;
1212                                 precache_model (defaultmodel);
1213                                 setmodel (self, defaultmodel);
1214                                 setsize (self, m1, m2);
1215                         }
1216
1217                         if (self.skin != stof(cvar_string("sv_defaultplayerskin")))
1218                                 self.skin = stof(cvar_string("sv_defaultplayerskin"));
1219                 } else {
1220                         if (self.playermodel != self.model)
1221                         {
1222                                 self.playermodel = CheckPlayerModel(self.playermodel);
1223                                 m1 = self.mins;
1224                                 m2 = self.maxs;
1225                                 precache_model (self.playermodel);
1226                                 setmodel (self, self.playermodel);
1227                                 setsize (self, m1, m2);
1228                         }
1229
1230                         if (self.skin != stof(self.playerskin))
1231                                 self.skin = stof(self.playerskin);
1232                 }
1233                 // Savage: Check for nameless players
1234                 if (strlen(self.netname) < 1) {
1235                         self.netname = "Player";
1236                         stuffcmd(self, "name Player\n");
1237                 }
1238
1239                 GrapplingHookFrame();
1240
1241                 W_WeaponFrame();
1242
1243                 if (self.button4 || (self.weapon == WEP_NEX && self.button3))
1244                 {
1245                         if (cvar("g_minstagib") && self.button3)
1246                         {
1247                                 if (self.jump_interval <= (time + 0.1))
1248                                 {
1249                                         self.jump_interval = time + 1;
1250                                         weapon_doattack(laser_check, laser_check, W_Laser_Attack);
1251                                 }
1252                         }
1253                         else if (self.viewzoom > 0.4)
1254                                 self.viewzoom = max (0.4, self.viewzoom - frametime * 2);
1255                 }
1256                 else if (self.viewzoom < 1.0)
1257                         self.viewzoom = min (1.0, self.viewzoom + frametime);
1258
1259
1260                 if (self.button2)
1261                         PlayerJump ();
1262                 else
1263                         self.flags = self.flags | FL_JUMPRELEASED;
1264
1265                 player_powerups();
1266                 player_regen();
1267                 player_anim();
1268
1269                 //self.angles_y=self.v_angle_y + 90;   // temp
1270
1271                 if (self.waterlevel == 2)
1272                         CheckWaterJump ();
1273
1274                 //if (TetrisPreFrame()) return;
1275         } else if(gameover) {
1276                 if (intermission_running)
1277                         IntermissionThink ();   // otherwise a button could be missed between
1278                 return;
1279         } else if(self.classname == "observer") {
1280
1281                 if (self.flags & FL_JUMPRELEASED) {
1282                         if (self.button2 && self.version == cvar("gameversion")) {
1283                                 if(!cvar("teamplay")) {
1284                                         self.flags = self.flags & !FL_JUMPRELEASED;
1285                                         self.classname = "player";
1286                                         if(!cvar("g_lms"))
1287                                                 bprint (strcat("^4", self.netname, "^4 is playing now\n"));
1288                                         PutClientInServer();
1289                                         centerprint(self,"");
1290                                         return;
1291                                 } else {
1292                                         self.flags = self.flags & !FL_JUMPRELEASED;
1293                                         stuffcmd(self,"menu_showteamselect\n");
1294                                         return;
1295                                 }
1296                         } else if(self.button0 && self.version == cvar("gameversion")) {
1297                                 self.flags = self.flags & !FL_JUMPRELEASED;
1298                                 if(SpectateNext() == 1) {
1299                                         self.classname = "spectator";
1300                                 }
1301                         }
1302                 } else {
1303                         if (!(self.button0 || self.button2)) {
1304                                 self.flags = self.flags | FL_JUMPRELEASED;
1305                         }
1306                 }
1307                 if(cvar("g_lms") && self.frags <= 0 && self.frags > -666)
1308                         centerprint(self, "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n^1You have no more lives left\nwait for next round\n\n\n^7press attack to spectate other players");
1309                 else if(cvar("g_lms") && self.frags == -666)
1310                         centerprint(self, "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n^1Match has already begun\nwait for next round\n\n\n^7press attack to spectate other players");
1311                 else
1312                         PrintWelcomeMessage(self);
1313                         //centerprint(self, "\n\n\npress jump to play\npress attack to spectate other players");
1314         } else if(self.classname == "spectator") {
1315
1316                 if (self.flags & FL_JUMPRELEASED) {
1317                         if (self.button2 && self.version == cvar("gameversion")) {
1318                                 if(!cvar("teamplay")) {
1319                                         self.flags = self.flags & !FL_JUMPRELEASED;
1320                                         self.classname = "player";
1321                                         if(!cvar("g_lms"))
1322                                                 bprint (strcat("^4", self.netname, "^4 is playing now\n"));
1323
1324                                         msg_entity = self;
1325                                         WriteByte(MSG_ONE, SVC_SETVIEW);
1326                                         WriteEntity(MSG_ONE, self);
1327                                         PutClientInServer();
1328                                         centerprint(self,"");
1329                                         return;
1330                                 } else {
1331                                         self.flags = self.flags & !FL_JUMPRELEASED;
1332                                         stuffcmd(self,"menu_showteamselect\n");
1333                                         return;
1334                                 }
1335                         } else if(self.button0) {
1336                                 self.flags = self.flags & !FL_JUMPRELEASED;
1337                                 if(SpectateNext() == 1) {
1338                                         self.classname = "spectator";
1339                                 } else {
1340                                         self.classname = "observer";
1341                                         msg_entity = self;
1342                                         WriteByte(MSG_ONE, SVC_SETVIEW);
1343                                         WriteEntity(MSG_ONE, self);
1344                                         PutClientInServer();
1345                                 }
1346                         } else if (self.button3) {
1347                                 self.flags = self.flags & !FL_JUMPRELEASED;
1348                                 self.classname = "observer";
1349                                 msg_entity = self;
1350                                 WriteByte(MSG_ONE, SVC_SETVIEW);
1351                                 WriteEntity(MSG_ONE, self);
1352                                 PutClientInServer();
1353                         } else {
1354                                 SpectateUpdate();
1355                         }
1356         } else {
1357                 if (!(self.button0 || self.button3)) {
1358                         self.flags = self.flags | FL_JUMPRELEASED;
1359                 }
1360                 }
1361                 if (cvar("g_lms") && self.frags < 1)
1362                         centerprint(self, strcat("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nspectating ", self.enemy.netname, "\n\n\n^7press attack for next player\npress attack2 for free fly mode"));
1363                 else
1364                         centerprint(self, strcat("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nspectating ", self.enemy.netname, "\n\n\n^7press jump to play\n^7press attack for next player\npress attack2 for free fly mode"));
1365
1366         }
1367 }
1368
1369
1370 /*
1371 =============
1372 PlayerPostThink
1373
1374 Called every frame for each client after the physics are run
1375 =============
1376 */
1377 void PlayerPostThink (void)
1378 {
1379         if(self.classname == "player") {
1380                 CheckRules_Player();
1381                 UpdateChatBubble();
1382                 UpdateTeamBubble();
1383                 UpdatePlayerColors();
1384                 if (self.deadflag == DEAD_NO)
1385                 if (self.impulse)
1386                         ImpulseCommands ();
1387                 if (intermission_running)
1388                         return;         // intermission or finale
1389
1390                 //PrintWelcomeMessage(self);
1391                 //if (TetrisPostFrame()) return;
1392         } else if (self.classname == "observer") {
1393                 //do nothing
1394         } else if (self.classname == "spectator") {
1395                 //do nothing
1396         }
1397 }