]> icculus.org git repositories - divverent/nexuiz.git/blob - data/qcsrc/gamec/cl_client.c
weaponsounds, defaultmodel, electrocombo, specmode and weaponmodel colors
[divverent/nexuiz.git] / data / qcsrc / gamec / cl_client.c
1 void info_player_start (void)
2 {
3         self.classname = "info_player_deathmatch";
4 }
5
6 void info_player_deathmatch (void)
7 {
8 }
9
10 float spawn_goodspots, spawn_badspots;
11 entity Spawn_ClassifyPoints(entity firstspot, entity playerlist, float mindist, float goodspotnum, float badspotnum)
12 {
13         local entity spot, player;
14         local float pcount;
15         spawn_goodspots = 0;
16         spawn_badspots = 0;
17         spot = firstspot;
18         while (spot)
19         {
20                 pcount = 0;
21                 player = playerlist;
22                 while (player)
23                 {
24                         if (player != self)
25                         if (vlen(player.origin - spot.origin) < 100)
26                                 pcount = pcount + 1;
27                         player = player.chain;
28                 }
29                 if (pcount)
30                 {
31                         if (spawn_goodspots >= badspotnum)
32                                 return spot;
33                         spawn_badspots = spawn_badspots + 1;
34                 }
35                 else
36                 {
37                         if (spawn_goodspots >= goodspotnum)
38                                 return spot;
39                         spawn_goodspots = spawn_goodspots + 1;
40                 }
41                 spot = find(spot, classname, "info_player_deathmatch");
42         }
43         return firstspot;
44 }
45
46 entity Spawn_FurthestPoint(entity firstspot, entity playerlist)
47 {
48         local entity best, spot, player;
49         local float bestrating, rating;
50         best = world;
51         bestrating = -1000000;
52         spot = firstspot;
53         while (spot)
54         {
55                 rating = 1000000000;
56                 player = playerlist;
57                 while (player)
58                 {
59                         if (player != self)
60                                 rating = min(rating, vlen(player.origin - spot.origin));
61                         player = player.chain;
62                 }
63                 rating = rating + random() * 16;
64                 if (bestrating < rating)
65                 {
66                         best = spot;
67                         bestrating = rating;
68                 }
69                 spot = find(spot, classname, "info_player_deathmatch");
70         }
71         return best;
72 }
73
74 /*
75 =============
76 SelectSpawnPoint
77
78 Finds a point to respawn
79 =============
80 */
81 entity SelectSpawnPoint (float anypoint)
82 {
83         local entity spot, firstspot, playerlist;
84         string spotname;
85
86         spot = find (world, classname, "testplayerstart");
87         if (spot)
88                 return spot;
89
90         spotname = "info_player_deathmatch";
91
92         if(!anypoint && cvar("g_ctf") )
93         {
94                 if(self.team == 5)//4)
95                         spotname = "info_player_team1";
96                 if(self.team == 14)//13)
97                         spotname = "info_player_team2";
98                 if(self.team == 4)//3)
99                         spotname = "info_player_team3";
100                 if(self.team == 13)//12)
101                         spotname = "info_player_team4";
102         }
103
104         playerlist = findchain(classname, "player");
105         firstspot = find(world, classname, spotname);
106         Spawn_ClassifyPoints(firstspot, playerlist, 100, 1000000, 1000000);
107         // first check if there are ANY good spots
108         if (spawn_goodspots > 0)
109         {
110                 // good spots exist, there is 50/50 chance of choosing a random good
111                 // spot or the furthest spot
112                 // (this means that roughly every other spawn will be furthest, so you
113                 // usually won't get fragged at spawn twice in a row)
114                 if (random() > 0.5)
115                         spot = Spawn_ClassifyPoints(firstspot, playerlist, 100, min(floor(random() * spawn_goodspots), spawn_goodspots - 1), 1000000);
116                 else
117                         spot = Spawn_FurthestPoint(firstspot, playerlist);
118         }
119         else
120         {
121                 // no good spots exist, pick a random bad spot
122                 spot = Spawn_ClassifyPoints(firstspot, playerlist, 100, 1000000, min(floor(random() * spawn_badspots), spawn_badspots - 1));
123         }
124
125         if (!spot)
126         {
127                 if(anypoint)
128                         error ("PutClientInServer: no start points on level");
129                 else // try again with deathmatch spots
130                         spot = SelectSpawnPoint(TRUE);
131         }
132
133         return spot;
134 }
135
136 /*
137 =============
138 CheckPlayerModel
139
140 Checks if the argument string can be a valid playermodel.
141 Returns a valid one in doubt.
142 =============
143 */
144 string CheckPlayerModel(string plyermodel) {
145         if( substring(plyermodel,0,14) != "models/player/") plyermodel = "models/player/marine.zym";
146
147         /* Possible Fixme: Check if server can open the model?
148            This would kill custom models, however. */
149
150         return plyermodel;
151 }
152
153 /*
154 =============
155 PutObserverInServer
156
157 putting a client as observer in the server
158 =============
159 */
160 void PutObserverInServer (void)
161 {
162         entity  spot;
163         spot = SelectSpawnPoint (FALSE);
164         RemoveGrapplingHook(self); // Wazat's Grappling Hook
165
166         self.classname = "observer";
167         self.health = 666;
168         self.takedamage = DAMAGE_NO;
169         self.solid = SOLID_NOT;
170         self.movetype = MOVETYPE_NOCLIP;
171         self.armorvalue = 666;
172         self.effects = 0;
173         self.armorvalue = cvar("g_balance_armor_start");
174         self.pauserotarmor_finished = 0;
175         self.pauserothealth_finished = 0;
176         self.pauseregen_finished = 0;
177         self.damageforcescale = 0;
178         self.death_time = 0;
179         self.dead_time = 0;
180         self.dead_frame = 0;
181         self.die_frame = 0;
182         self.alpha = 0;
183         self.scale = 0;
184         self.fade_time = 0;
185         self.pain_frame = 0;
186         self.pain_finished = 0;
187         self.strength_finished = 0;
188         self.invincible_finished = 0;
189         self.pushltime = 0;
190         self.vote_finished = 0;
191         self.think = SUB_Null;
192         self.nextthink = 0;
193         self.hook_time = 0;
194         self.runes = 0;
195         self.deadflag = DEAD_NO;
196         self.angles = spot.angles;
197         self.angles_z = 0; 
198         self.fixangle = TRUE; 
199         self.crouch = FALSE;
200         self.view_ofs = PL_VIEW_OFS;
201         setorigin (self, spot.origin + '0 0 1' * (1 - self.mins_z - 24));
202         self.oldorigin = self.origin;
203         self.items = 0;
204         self.model = "";
205         self.modelindex = 0;
206         self.weapon = 0;
207         self.weaponmodel = "";
208         self.weaponframe = 0;
209         self.weaponentity = world;
210         self.killcount = -666;
211         self.frags = -666;
212         stuffcmd(self, "set viewsize 120 \n");
213         bprint (strcat("^4", self.netname, " is spectating now\n"));
214 }
215
216
217 /*
218 =============
219 PutClientInServer
220
221 Called when a client spawns in the server
222 =============
223 */
224 void PutClientInServer (void)
225 {       
226         if(self.classname == "player") {
227                 entity  spot;
228
229                 spot = SelectSpawnPoint (FALSE);
230                 
231                 RemoveGrapplingHook(self); // Wazat's Grappling Hook
232         
233                 self.classname = "player";
234                 self.iscreature = TRUE;
235                 self.movetype = MOVETYPE_WALK;
236                 self.solid = SOLID_SLIDEBOX;
237                 self.flags = FL_CLIENT;
238                 self.takedamage = DAMAGE_AIM;
239                 self.effects = 0;
240                 self.health = cvar("g_balance_health_start");
241                 self.armorvalue = cvar("g_balance_armor_start");
242                 self.pauserotarmor_finished = time + 10;
243                 self.pauserothealth_finished = time + 10;
244                 self.pauseregen_finished = 0;
245                 self.damageforcescale = 2;
246                 self.death_time = 0;
247                 self.dead_time = 0;
248                 self.dead_frame = 0;
249                 self.die_frame = 0;
250                 self.alpha = 0;
251                 self.scale = 0;
252                 self.fade_time = 0;
253                 self.pain_frame = 0;
254                 self.pain_finished = 0;
255                 self.strength_finished = 0;
256                 self.invincible_finished = 0;
257                 self.pushltime = 0;
258                 //self.speed_finished = 0;
259                 //self.slowmo_finished = 0;
260                 self.vote_finished = 0;
261                 // players have no think function
262                 self.think = SUB_Null;
263                 self.nextthink = 0;
264
265                 self.hook_time = 0;
266         
267                 self.runes = 0;
268
269                 self.deadflag = DEAD_NO;
270
271                 self.angles = spot.angles;
272
273                 self.angles_z = 0; // never spawn tilted even if the spot says to
274                 self.fixangle = TRUE; // turn this way immediately
275                 self.velocity = '0 0 0';
276                 self.avelocity = '0 0 0';
277                 self.punchangle = '0 0 0';
278                 self.punchvector = '0 0 0';
279                 self.oldvelocity = self.velocity;
280         
281                 self.viewzoom = 0.6;
282
283                 if(cvar("sv_defaultcharacter") == 1) {
284                         local string defaultmodel;
285                         defaultmodel = CheckPlayerModel(cvar_string("sv_defaultplayermodel"));
286
287                         precache_model (defaultmodel);
288                         setmodel (self, defaultmodel);
289                         self.skin = stof(cvar_string("sv_defaultplayerskin"));
290                 } else {
291                         self.playermodel = CheckPlayerModel(self.playermodel);
292
293                         precache_model (self.playermodel);
294                         setmodel (self, self.playermodel);
295                         self.skin = stof(self.playerskin);
296                 
297                 }
298                 
299                 self.crouch = FALSE;
300                 self.view_ofs = PL_VIEW_OFS;
301                 setsize (self, PL_MIN, PL_MAX);
302                 setorigin (self, spot.origin + '0 0 1' * (1 - self.mins_z - 24));
303                 // don't reset back to last position, even if new position is stuck in solid
304                 self.oldorigin = self.origin;
305
306                 if (cvar("g_use_ammunition")) {
307                         self.ammo_shells = cvar("g_start_ammo_shells");
308                         self.ammo_nails = cvar("g_start_ammo_nails");
309                         self.ammo_rockets = cvar("g_start_ammo_rockets");
310                         self.ammo_cells = cvar("g_start_ammo_cells");
311                 } else {
312                         self.ammo_shells = 999;
313                         self.ammo_nails = 999;
314                         self.ammo_rockets = 999;
315                         self.ammo_cells = 999;
316                 }
317
318                 self.items = 0;
319                 if (cvar("g_start_weapon_laser"))
320                 {
321                         self.items = self.items | IT_LASER;
322                         self.switchweapon = WEP_LASER;
323                 }
324                 if (cvar("g_start_weapon_shotgun"))
325                 {
326                         self.items = self.items | IT_SHOTGUN;
327                         self.switchweapon = WEP_SHOTGUN;
328                 }
329                 if (cvar("g_start_weapon_uzi"))
330                 {
331                         self.items = self.items | IT_UZI;
332                         self.switchweapon = WEP_UZI;
333                 }
334                 if (cvar("g_start_weapon_grenadelauncher"))
335                 {
336                         self.items = self.items | IT_GRENADE_LAUNCHER;
337                         self.switchweapon = WEP_GRENADE_LAUNCHER;
338                 }
339                 if (cvar("g_start_weapon_electro"))
340                 {
341                         self.items = self.items | IT_ELECTRO;
342                         self.switchweapon = WEP_ELECTRO;
343                 }
344                 if (cvar("g_start_weapon_crylink"))
345                 {
346                         self.items = self.items | IT_CRYLINK;
347                         self.switchweapon = WEP_CRYLINK;
348                 }
349                 if (cvar("g_start_weapon_nex"))
350                 {
351                         self.items = self.items | IT_NEX;
352                         self.switchweapon = WEP_NEX;
353                 }
354                 if (cvar("g_start_weapon_hagar"))
355                 {
356                         self.items = self.items | IT_HAGAR;
357                         self.switchweapon = WEP_HAGAR;
358                 }
359                 if (cvar("g_start_weapon_rocketlauncher"))
360                 {
361                         self.items = self.items | IT_ROCKET_LAUNCHER;
362                         self.switchweapon = WEP_ROCKET_LAUNCHER;
363                 }
364         
365                 self.event_damage = PlayerDamage;
366         
367                 self.statdraintime = time + 5;
368                 self.button0 = self.button1 = self.button2 = self.button3 = 0;
369         
370                 if(self.frags == -666 && self.killcount == -666) {
371                         self.killcount = 0;
372                         self.frags = 0;
373                 }
374         
375                 /*
376                 W_UpdateWeapon();
377                 W_UpdateAmmo();
378                 */
379                 CL_SpawnWeaponentity();
380         
381                 //stuffcmd(self, "chase_active 0");
382                 stuffcmd(self, "set viewsize $tmpviewsize \n");
383         } else if(self.classname == "observer") {
384                 PutObserverInServer ();
385         }
386 }
387
388 /*
389 =============
390 SetNewParms
391 =============
392 */
393 void SetNewParms (void)
394 {
395
396 }
397
398 /*
399 =============
400 SetChangeParms
401 =============
402 */
403 void SetChangeParms (void)
404 {
405
406 }
407
408 /*
409 =============
410 ClientKill
411
412 Called when a client types 'kill' in the console
413 =============
414 */
415 void ClientKill (void)
416 {
417         Damage(self, self, self, 100000, DEATH_KILL, self.origin, '0 0 0');
418 }
419
420 /*
421 =============
422 ClientConnect
423
424 Called when a client connects to the server
425 =============
426 */
427 string ColoredTeamName(float t);
428 //void dom_player_join_team(entity pl);
429 void ClientConnect (void)
430 {
431         self.classname = "player_joining";
432
433         //if(cvar("g_domination"))
434         //      dom_player_join_team(self);
435
436         JoinBestTeam(self, FALSE);
437
438         if(cvar("sv_spectate") == 1) {
439                 self.classname = "observer";    
440         } else {
441                 self.classname = "player";              
442         }
443         
444         stuffcmd(self, "set tmpviewsize $viewsize \n");
445         
446         bprint ("^4",self.netname);
447         bprint (" connected");
448
449         if(cvar("g_domination") || cvar("g_ctf"))
450         {
451                 bprint(" and joined the ");
452                 bprint(ColoredTeamName(self.team));
453         }
454
455         bprint("\n");
456
457         self.welcomemessage_time = time + cvar("welcome_message_time");
458         self.welcomemessage_time2 = 0;
459
460         stuffcmd(self, strcat("exec maps/", mapname, ".cfg\n"));
461         // send prediction settings to the client
462         stuffcmd(self, strcat("cl_movement_maxspeed ", ftos(cvar("sv_maxspeed")), "\n"));
463         stuffcmd(self, strcat("cl_movement_maxairspeed ", ftos(cvar("sv_maxairspeed")), "\n"));
464         stuffcmd(self, strcat("cl_movement_accelerate ", ftos(cvar("sv_accelerate")), "\n"));
465         stuffcmd(self, strcat("cl_movement_friction ", ftos(cvar("sv_friction")), "\n"));
466         stuffcmd(self, strcat("cl_movement_stopspeed ", ftos(cvar("sv_stopspeed")), "\n"));
467         stuffcmd(self, strcat("cl_movement_jumpvelocity ", ftos(cvar("g_balance_jumpheight")), "\n"));
468         stuffcmd(self, strcat("cl_movement_stepheight ", ftos(cvar("sv_stepheight")), "\n"));
469         stuffcmd(self, strcat("cl_movement_edgefriction 0\n"));
470         
471         // Wazat's grappling hook
472         SetGrappleHookBindings();       
473
474         // get autoswitch state from player
475         stuffcmd(self, "alias autoswitch \"set cl_autoswitch $1; cmd autoswitch $1\"\n");
476         stuffcmd(self, "cmd autoswitch $cl_autoswitch\n");
477
478         // get version info from player
479         stuffcmd(self, "cmd clientversion $g_nexuizversion\n");
480 }
481
482 /*
483 =============
484 ClientDisconnect
485
486 Called when a client disconnects from the server
487 =============
488 */
489 .entity chatbubbleentity;
490 void ClientDisconnect (void)
491 {
492         bprint ("^4",self.netname);
493         bprint (" disconnected\n");
494
495         if (self.chatbubbleentity)
496         {
497                 remove (self.chatbubbleentity);
498                 self.chatbubbleentity = world;
499         }
500         DropAllRunes(self);
501         stuffcmd(self, "set viewsize $tmpviewsize \n");
502 }
503
504 .float buttonchat;
505 void() ChatBubbleThink =
506 {
507         self.nextthink = time;
508         if (!self.owner.modelindex || self.owner.chatbubbleentity != self)
509         {
510                 remove(self);
511                 return;
512         }
513         setorigin(self, self.owner.origin + '0 0 15' + self.owner.maxs_z * '0 0 1');
514         if (self.owner.buttonchat && !self.owner.deadflag)
515                 self.model = self.mdl;
516         else
517                 self.model = "";
518 };
519
520 void() UpdateChatBubble =
521 {
522         if (!self.modelindex)
523                 return;
524         // spawn a chatbubble entity if needed
525         if (!self.chatbubbleentity)
526         {
527                 self.chatbubbleentity = spawn();
528                 self.chatbubbleentity.owner = self;
529                 self.chatbubbleentity.exteriormodeltoclient = self;
530                 self.chatbubbleentity.think = ChatBubbleThink;
531                 self.chatbubbleentity.nextthink = time;
532                 setmodel(self.chatbubbleentity, "models/misc/chatbubble.spr");
533                 setorigin(self.chatbubbleentity, self.origin + '0 0 15' + self.maxs_z * '0 0 1');
534                 self.chatbubbleentity.mdl = self.chatbubbleentity.model;
535                 self.chatbubbleentity.model = "";
536         }
537 }
538
539 // LordHavoc: this hack will be removed when proper _pants/_shirt layers are
540 // added to the model skins
541 /*void() UpdateColorModHack =
542 {
543         local float c;
544         c = self.clientcolors & 15;
545         // LordHavoc: only bothering to support white, green, red, yellow, blue
546              if (teamplay == 0) self.colormod = '0 0 0';
547         else if (c ==  0) self.colormod = '1.00 1.00 1.00';
548         else if (c ==  3) self.colormod = '0.10 1.73 0.10';
549         else if (c ==  4) self.colormod = '1.73 0.10 0.10';
550         else if (c == 12) self.colormod = '1.22 1.22 0.10';
551         else if (c == 13) self.colormod = '0.10 0.10 1.73';
552         else self.colormod = '1 1 1';
553 };*/
554
555 void UpdatePlayerColors () {
556         if(self.weaponentity) {
557                 self.weaponentity.colormap = self.colormap;
558                 self.exteriorweaponentity.colormap = self.colormap;
559         }
560 }
561 /*
562 =============
563 PlayerJump
564
565 When you press the jump key
566 =============
567 */
568 void PlayerJump (void)
569 {
570         float mjumpheight;
571
572         mjumpheight = cvar("g_balance_jumpheight");
573         if (self.waterlevel >= 2)
574         {
575                 if (self.watertype == CONTENT_WATER)
576                         self.velocity_z = 200;
577                 else if (self.watertype == CONTENT_SLIME)
578                         self.velocity_z = 80;
579                 else
580                         self.velocity_z = 50;
581
582                 return;
583         }
584
585
586         if (!(self.flags & FL_ONGROUND))
587                 return;
588
589         if (!(self.flags & FL_JUMPRELEASED))
590                 return;
591
592         if(cvar("g_runematch"))
593         {
594                 if(self.runes & RUNE_SPEED)
595                 {
596                         if(self.runes & CURSE_SLOW)
597                                 mjumpheight = mjumpheight * cvar("g_balance_rune_speed_combo_jumpheight");
598                         else
599                                 mjumpheight = mjumpheight * cvar("g_balance_rune_speed_jumpheight");
600                 }
601                 else if(self.runes & CURSE_SLOW)
602                 {
603                         mjumpheight = mjumpheight * cvar("g_balance_curse_slow_jumpheight");
604                 }
605         }
606
607         self.velocity_z = self.velocity_z + mjumpheight;
608         self.oldvelocity_z = self.velocity_z;
609
610         self.flags = self.flags - FL_ONGROUND;
611         self.flags = self.flags - FL_JUMPRELEASED;
612 }
613
614 void() CheckWaterJump =
615 {
616         local vector start, end;
617
618 // check for a jump-out-of-water
619         makevectors (self.angles);
620         start = self.origin;
621         start_z = start_z + 8;
622         v_forward_z = 0;
623         normalize(v_forward);
624         end = start + v_forward*24;
625         traceline (start, end, TRUE, self);
626         if (trace_fraction < 1)
627         {       // solid at waist
628                 start_z = start_z + self.maxs_z - 8;
629                 end = start + v_forward*24;
630                 self.movedir = trace_plane_normal * -50;
631                 traceline (start, end, TRUE, self);
632                 if (trace_fraction == 1)
633                 {       // open at eye level
634                         self.flags = self.flags | FL_WATERJUMP;
635                         self.velocity_z = 225;
636                         self.flags = self.flags - (self.flags & FL_JUMPRELEASED);
637                         self.teleport_time = time + 2;  // safety net
638                         return;
639                 }
640         }
641 };
642
643
644 void respawn(void)
645 {
646         CopyBody(1);
647         PutClientInServer();
648 }
649
650 void player_powerups (void)
651 {
652         self.effects = self.effects - (self.effects & (EF_RED | EF_BLUE | EF_ADDITIVE | EF_FULLBRIGHT));
653         if (self.items & IT_STRENGTH)
654         {
655                 self.effects = self.effects | (EF_BLUE | EF_ADDITIVE | EF_FULLBRIGHT);
656                 if (time > self.strength_finished)
657                 {
658                         self.items = self.items - (self.items & IT_STRENGTH);
659                         sprint(self, "^3Strength has worn off\n");
660                 }
661         }
662         else
663         {
664                 if (time < self.strength_finished)
665                 {
666                         self.items = self.items | IT_STRENGTH;
667                         sprint(self, "^3Strength infuses your weapons with devestating power\n");
668                 }
669         }
670         if (self.items & IT_INVINCIBLE)
671         {
672                 self.effects = self.effects | (EF_RED | EF_ADDITIVE | EF_FULLBRIGHT);
673                 if (time > self.invincible_finished)
674                 {
675                         self.items = self.items - (self.items & IT_INVINCIBLE);
676                         sprint(self, "^3Shield has worn off\n");
677                 }
678         }
679         else
680         {
681                 if (time < self.invincible_finished)
682                 {
683                         self.items = self.items | IT_INVINCIBLE;
684                         sprint(self, "^3Shield surrounds you\n");
685                 }
686         }
687         
688         if (cvar("g_fullbrightplayers"))
689                 self.effects = EF_FULLBRIGHT;
690         
691 }
692
693 void player_regen (void)
694 {
695         float maxh, maxa, max_mod, regen_mod, rot_mod;
696         maxh = cvar("g_balance_health_stable");
697         maxa = cvar("g_balance_armor_stable");
698
699         if(cvar("g_runematch"))
700         {
701                 max_mod = regen_mod = rot_mod = 1;
702                 if (self.runes & RUNE_REGEN)
703                 {
704                         if (self.runes & CURSE_VENOM) // do we have both rune/curse?
705                         {
706                                 regen_mod = cvar("g_balance_rune_regen_combo_regenrate");
707                                 max_mod = cvar("g_balance_rune_regen_combo_hpmod");
708                         }
709                         else
710                         {
711                                 regen_mod = cvar("g_balance_rune_regen_regenrate");
712                                 max_mod = cvar("g_balance_rune_regen_hpmod");
713                         }
714                 }
715                 else if (self.runes & CURSE_VENOM)
716                 {
717                         max_mod = cvar("g_balance_curse_venom_hpmod");
718                         if (self.runes & RUNE_REGEN) // do we have both rune/curse?
719                                 rot_mod = cvar("g_balance_rune_regen_combo_rotrate");
720                         else
721                                 rot_mod = cvar("g_balance_curse_venom_rotrate");
722                         //if (!self.runes & RUNE_REGEN)
723                         //      rot_mod = cvar("g_balance_curse_venom_rotrate");
724                 }
725                 maxh = maxh * max_mod;
726                 //maxa = maxa * max_mod;
727
728                 if (time > self.pauserotarmor_finished)
729                 {
730                         if (self.armorvalue > maxa)
731                                 self.armorvalue = bound(0, self.armorvalue + (maxa - self.armorvalue) * cvar("g_balance_armor_rot") * frametime, 1000);
732                 }
733                 if (time > self.pauserothealth_finished)
734                 {
735                         if (self.health > maxh)
736                                 self.health = bound(0, self.health + (maxh - self.health) * rot_mod*cvar("g_balance_health_rot") * frametime, 1000);
737                 }
738                 if (time > self.pauseregen_finished)
739                 {
740                         if (self.health < maxh)
741                                 self.health = bound(0, self.health + (maxh- self.health) * regen_mod*cvar("g_balance_health_regen") * frametime, 1000);
742                         if (self.armorvalue < maxa)
743                                 self.armorvalue = bound(0, self.armorvalue + (maxa - self.armorvalue) * cvar("g_balance_armor_regen") * frametime, 1000);
744                 }
745         }
746         else
747         {
748                 if (time > self.pauserothealth_finished)
749                 if (self.health > maxh)
750                         self.health = bound(0, self.health + (maxh - self.health) * cvar("g_balance_health_rot") * frametime, 1000);
751                 if (time > self.pauserotarmor_finished)
752                 if (self.armorvalue > maxa)
753                         self.armorvalue = bound(0, self.armorvalue + (maxa - self.armorvalue) * cvar("g_balance_armor_rot") * frametime, 1000);
754                 if (time > self.pauseregen_finished)
755                 {
756                         if (self.health < maxh)
757                                 self.health = bound(0, self.health + (maxh- self.health) * cvar("g_balance_health_regen") * frametime, 1000);
758                         if (self.armorvalue < maxa)
759                                 self.armorvalue = bound(0, self.armorvalue + (maxa - self.armorvalue) * cvar("g_balance_armor_regen") * frametime, 1000);
760                 }
761         }
762 }
763
764 /*
765 ======================
766 spectate mode routines
767 ======================
768 */
769 void SpectateCopy(entity spectatee) {
770         self.armortype = spectatee.armortype;
771         self.armorvalue = spectatee.armorvalue;
772         self.currentammo = spectatee.currentammo;
773         self.effects = spectatee.effects;
774         self.health = spectatee.health;
775         self.impulse = 0;
776         self.items = spectatee.items;
777         self.punchangle = spectatee.punchangle;
778         self.view_ofs = spectatee.view_ofs;
779         self.v_angle = spectatee.v_angle;
780         self.viewzoom = spectatee.viewzoom;
781         setorigin(self, spectatee.origin);
782         setsize(self, spectatee.mins, spectatee.maxs);
783 }
784
785 void SpectateUpdate() {
786         if (self != self.enemy) {
787                 SpectateCopy(self.enemy);
788                 msg_entity = self;
789                 WriteByte(MSG_ONE, SVC_SETANGLE);
790                 WriteAngle(MSG_ONE, self.enemy.v_angle_x);
791                 WriteAngle(MSG_ONE, self.enemy.v_angle_y);
792                 WriteAngle(MSG_ONE, self.enemy.v_angle_z);
793         }
794 }
795
796 float SpectateNext() {
797         other = find(self.enemy, classname, "player");
798         if (!other) {
799                 other = find(other, classname, "player");
800         }
801         if (other) {
802                 self.enemy = other;
803         }
804         if(self.enemy.classname == "player") {
805                 msg_entity = self;
806                 WriteByte(MSG_ONE, SVC_SETVIEW);
807                 WriteEntity(MSG_ONE, self.enemy);
808                 stuffcmd(self, "set viewsize $tmpviewsize \n");
809                 SpectateUpdate();
810                 return 1;
811         } else {
812                 return 0;
813         }
814 }
815
816 /*
817 =============
818 PlayerPreThink
819
820 Called every frame for each client before the physics are run
821 =============
822 */
823 void PlayerPreThink (void)
824 {
825         if(self.classname == "player") {
826                 local vector m1, m2;
827
828 //              MauveBot_AI();
829
830 //              if(self.netname == "Wazat")
831 //                      bprint(strcat(self.classname, "\n"));
832
833                 CheckRules_Player();
834
835                 if (intermission_running)
836                 {
837                         IntermissionThink ();   // otherwise a button could be missed between
838                         return;                                 // the think tics
839                 }
840
841                 if (self.deadflag != DEAD_NO)
842                 {
843                         player_anim();
844                         weapon_freeze();
845                         if (self.deadflag == DEAD_DYING)
846                         {
847                                 if (time > self.dead_time)
848                                         self.deadflag = DEAD_DEAD;
849                         }
850                         else if (self.deadflag == DEAD_DEAD)
851                         {
852                                 if (!self.button0 && !self.button2 && !self.button3)
853                                         self.deadflag = DEAD_RESPAWNABLE;
854                         }
855                         else if (self.deadflag == DEAD_RESPAWNABLE)
856                         {
857                                 if (self.button0 || self.button2 || self.button3  || self.button4)
858                                         respawn();
859                         }
860                         return;
861                 }
862
863                 if (self.button5)
864                 {
865                         if (!self.crouch)
866                         {
867                                 self.crouch = TRUE;
868                                 self.view_ofs = PL_CROUCH_VIEW_OFS;
869                                 setsize (self, PL_CROUCH_MIN, PL_CROUCH_MAX);
870                         }
871                 }
872                 else
873                 {
874                         if (self.crouch)
875                         {
876                                 tracebox(self.origin, PL_MIN, PL_MAX, self.origin, FALSE, self);
877                                 if (!trace_startsolid)
878                                 {
879                                         self.crouch = FALSE;
880                                         self.view_ofs = PL_VIEW_OFS;
881                                         setsize (self, PL_MIN, PL_MAX);
882                                 }
883                         }
884                 }
885
886                 if(cvar("sv_defaultcharacter") == 1) {
887                         local string defaultmodel;
888                         defaultmodel = CheckPlayerModel(cvar_string("sv_defaultplayermodel"));
889                         
890                         if (defaultmodel != self.model)
891                         {
892                                 m1 = self.mins;
893                                 m2 = self.maxs;
894                                 precache_model (defaultmodel);
895                                 setmodel (self, defaultmodel);
896                                 setsize (self, m1, m2);
897                         }
898
899                         if (self.skin != stof(cvar_string("sv_defaultplayerskin")))
900                                 self.skin = stof(cvar_string("sv_defaultplayerskin"));
901                 } else {
902                         if (self.playermodel != self.model)
903                         {
904                                 self.playermodel = CheckPlayerModel(self.playermodel);
905                                 m1 = self.mins;
906                                 m2 = self.maxs;
907                                 precache_model (self.playermodel);
908                                 setmodel (self, self.playermodel);
909                                 setsize (self, m1, m2);
910                         }
911
912                         if (self.skin != stof(self.playerskin))
913                                 self.skin = stof(self.playerskin);
914                 }
915                 // Savage: Check for nameless players
916                 if (strlen(self.netname) < 1) {
917                         self.netname = "Player";
918                         stuffcmd(self, "name Player\n");
919                 }
920         
921                 GrapplingHookFrame();
922
923                 W_WeaponFrame();
924
925                 if (self.button4 || (self.weapon == WEP_NEX && self.button3))
926                 {
927                         if (self.viewzoom > 0.4)
928                                 self.viewzoom = max (0.4, self.viewzoom - frametime * 2);
929                 }
930                 else if (self.viewzoom < 1.0)
931                         self.viewzoom = min (1.0, self.viewzoom + frametime);
932
933
934                 if (self.button2)
935                         PlayerJump ();
936                 else
937                         self.flags = self.flags | FL_JUMPRELEASED;
938
939                 if (self.vote_finished > 0 // this player has called a vote
940                     && time > self.vote_finished) // time is up
941                 {
942                         VoteCount();
943                 }
944
945                 player_powerups();
946                 player_regen();
947                 player_anim();
948
949                 //self.angles_y=self.v_angle_y + 90;   // temp
950
951                 if (self.waterlevel == 2)
952                         CheckWaterJump ();
953
954                 //if (TetrisPreFrame()) return;
955         } else if(self.classname == "observer") {
956                 if (self.flags & FL_JUMPRELEASED) {                                                                                                                           
957                         if (self.button2) {
958                                 self.flags = self.flags & !FL_JUMPRELEASED;
959                                 self.classname = "player";
960                                 bprint (strcat("^4", self.netname, " is playing now\n"));
961                                 PutClientInServer();
962                                 centerprint(self,"");
963                                 return;
964                         } else if(self.button0) {
965                                 self.flags = self.flags & !FL_JUMPRELEASED;
966                                 if(SpectateNext() == 1) {
967                                         self.classname = "spectator";
968                                 } 
969                         }
970         } else {
971                 if (!(self.button0 || self.button2)) {
972                                 self.flags = self.flags | FL_JUMPRELEASED;
973                 }
974                 }
975                 centerprint(self, strcat("\n", "\n", "\n", "press jump to play", "\n", "press attack to spectate other players"));
976         } else if(self.classname == "spectator") {
977                 if (self.flags & FL_JUMPRELEASED) {
978                         if(self.button0) {
979                                 self.flags = self.flags & !FL_JUMPRELEASED;
980                                 if(SpectateNext() == 1) {
981                                         self.classname = "spectator";
982                                 } else {
983                                         self.classname = "observer";
984                                         msg_entity = self;                                                                                                                                                     
985                                         WriteByte(MSG_ONE, SVC_SETVIEW);                                                                                                                                       
986                                         WriteEntity(MSG_ONE, self);   
987                                         PutClientInServer();                                    
988                                 }
989                         } else if (self.button3) {
990                                 self.flags = self.flags & !FL_JUMPRELEASED;
991                                 self.classname = "observer";
992                                 msg_entity = self;                                                                                                                                                     
993                                 WriteByte(MSG_ONE, SVC_SETVIEW);                                                                                                                                       
994                                 WriteEntity(MSG_ONE, self);   
995                                 PutClientInServer();
996                         } else {
997                                 SpectateUpdate();
998                         }
999         } else {
1000                 if (!(self.button0 || self.button3)) {
1001                                 self.flags = self.flags | FL_JUMPRELEASED;
1002                 }
1003                 }
1004                 centerprint(self, strcat("spectating ", self.enemy.netname, "\n", "\n", "\n", "press attack for next player", "\n", "press attack2 for free fly mode"));
1005         }
1006         
1007 }
1008
1009 /*
1010 =============
1011 PlayerPostThink
1012
1013 Called every frame for each client after the physics are run
1014 =============
1015 */
1016 void PlayerPostThink (void)
1017 {
1018         if(self.classname == "player") {
1019                 CheckRules_Player();
1020                 UpdateChatBubble();
1021                 UpdatePlayerColors();
1022                 if (self.deadflag == DEAD_NO)
1023                 if (self.impulse)
1024                         ImpulseCommands ();
1025                 if (intermission_running)
1026                         return;         // intermission or finale
1027
1028                 PrintWelcomeMessage(self);
1029                 //if (TetrisPostFrame()) return;
1030         } else if (self.classname == "observer") {
1031                 //do nothing
1032         } else if (self.classname == "spectator") {
1033                 //do nothing
1034         }
1035 }