]> icculus.org git repositories - divverent/nexuiz.git/blob - data/qcsrc/gamec/cl_client.c
added minstagib mutator, killmessages and a first bunch of announcer messages (soundf...
[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, "^4 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                 if(cvar("g_minstagib"))
366                 {
367                         self.health = 100;
368                         self.armorvalue = 0;
369                         self.items = IT_NEX;
370                         self.switchweapon = WEP_NEX;
371                         self.ammo_cells = cvar("g_minstagib_ammo_start");
372                         self.extralives = 0;
373                         self.jump_interval = time;
374                         stuffcmd(self, strcat("crosshair_static ", self.crosshair_static, "\n"));
375                 }
376         
377                 self.event_damage = PlayerDamage;
378         
379                 self.statdraintime = time + 5;
380                 self.button0 = self.button1 = self.button2 = self.button3 = 0;
381         
382                 if(self.frags == -666 && self.killcount == -666) {
383                         self.killcount = 0;
384                         self.frags = 0;
385                 }
386         
387                 /*
388                 W_UpdateWeapon();
389                 W_UpdateAmmo();
390                 */
391                 CL_SpawnWeaponentity();
392         
393                 //stuffcmd(self, "chase_active 0");
394                 //stuffcmd(self, "set viewsize $tmpviewsize \n");
395         } else if(self.classname == "observer") {
396                 PutObserverInServer ();
397         }
398 }
399
400 /*
401 =============
402 SetNewParms
403 =============
404 */
405 void SetNewParms (void)
406 {
407
408 }
409
410 /*
411 =============
412 SetChangeParms
413 =============
414 */
415 void SetChangeParms (void)
416 {
417
418 }
419
420 /*
421 =============
422 ClientKill
423
424 Called when a client types 'kill' in the console
425 =============
426 */
427 void ClientKill (void)
428 {
429         Damage(self, self, self, 100000, DEATH_KILL, self.origin, '0 0 0');
430 }
431
432 /*
433 =============
434 ClientConnect
435
436 Called when a client connects to the server
437 =============
438 */
439 string ColoredTeamName(float t);
440 //void dom_player_join_team(entity pl);
441 void ClientConnect (void)
442 {
443         self.classname = "player_joining";
444
445         //if(cvar("g_domination"))
446         //      dom_player_join_team(self);
447
448         JoinBestTeam(self, FALSE);
449
450         if(cvar("sv_spectate") == 1) {
451                 self.classname = "observer";    
452         } else {
453                 self.classname = "player";              
454         }
455         
456         //stuffcmd(self, "set tmpviewsize $viewsize \n");
457         
458         bprint ("^4",self.netname);
459         bprint ("^4 connected");
460
461         if(cvar("g_domination") || cvar("g_ctf"))
462         {
463                 bprint(" and joined the ");
464                 bprint(ColoredTeamName(self.team));
465         }
466
467         bprint("\n");
468
469         self.welcomemessage_time = time + cvar("welcome_message_time");
470         self.welcomemessage_time2 = 0;
471
472         stuffcmd(self, strcat("exec maps/", mapname, ".cfg\n"));
473         // send prediction settings to the client
474         stuffcmd(self, strcat("cl_movement_maxspeed ", ftos(cvar("sv_maxspeed")), "\n"));
475         stuffcmd(self, strcat("cl_movement_maxairspeed ", ftos(cvar("sv_maxairspeed")), "\n"));
476         stuffcmd(self, strcat("cl_movement_accelerate ", ftos(cvar("sv_accelerate")), "\n"));
477         stuffcmd(self, strcat("cl_movement_friction ", ftos(cvar("sv_friction")), "\n"));
478         stuffcmd(self, strcat("cl_movement_stopspeed ", ftos(cvar("sv_stopspeed")), "\n"));
479         stuffcmd(self, strcat("cl_movement_jumpvelocity ", ftos(cvar("g_balance_jumpheight")), "\n"));
480         stuffcmd(self, strcat("cl_movement_stepheight ", ftos(cvar("sv_stepheight")), "\n"));
481         stuffcmd(self, strcat("cl_movement_edgefriction 0\n"));
482         // Wazat's grappling hook
483         SetGrappleHookBindings();       
484
485         // get autoswitch state from player
486         stuffcmd(self, "alias autoswitch \"set cl_autoswitch $1; cmd autoswitch $1\"\n");
487         stuffcmd(self, "cmd autoswitch $cl_autoswitch\n");
488
489         // get version info from player
490         stuffcmd(self, "cmd clientversion $g_nexuizversion\n");
491
492         // get crosshair_static
493         stuffcmd(self, "cmd crosshair $crosshair_static\n");
494 }
495
496 /*
497 =============
498 ClientDisconnect
499
500 Called when a client disconnects from the server
501 =============
502 */
503 .entity chatbubbleentity;
504 void ClientDisconnect (void)
505 {
506         bprint ("^4",self.netname);
507         bprint ("^4 disconnected\n");
508
509         if (self.chatbubbleentity)
510         {
511                 remove (self.chatbubbleentity);
512                 self.chatbubbleentity = world;
513         }
514         DropAllRunes(self);
515         //stuffcmd(self, "set viewsize $tmpviewsize \n");
516 }
517
518 .float buttonchat;
519 void() ChatBubbleThink =
520 {
521         self.nextthink = time;
522         if (!self.owner.modelindex || self.owner.chatbubbleentity != self)
523         {
524                 remove(self);
525                 return;
526         }
527         setorigin(self, self.owner.origin + '0 0 15' + self.owner.maxs_z * '0 0 1');
528         if (self.owner.buttonchat && !self.owner.deadflag)
529                 self.model = self.mdl;
530         else
531                 self.model = "";
532 };
533
534 void() UpdateChatBubble =
535 {
536         if (!self.modelindex)
537                 return;
538         // spawn a chatbubble entity if needed
539         if (!self.chatbubbleentity)
540         {
541                 self.chatbubbleentity = spawn();
542                 self.chatbubbleentity.owner = self;
543                 self.chatbubbleentity.exteriormodeltoclient = self;
544                 self.chatbubbleentity.think = ChatBubbleThink;
545                 self.chatbubbleentity.nextthink = time;
546                 setmodel(self.chatbubbleentity, "models/misc/chatbubble.spr");
547                 setorigin(self.chatbubbleentity, self.origin + '0 0 15' + self.maxs_z * '0 0 1');
548                 self.chatbubbleentity.mdl = self.chatbubbleentity.model;
549                 self.chatbubbleentity.model = "";
550         }
551 }
552
553 // LordHavoc: this hack will be removed when proper _pants/_shirt layers are
554 // added to the model skins
555 /*void() UpdateColorModHack =
556 {
557         local float c;
558         c = self.clientcolors & 15;
559         // LordHavoc: only bothering to support white, green, red, yellow, blue
560              if (teamplay == 0) self.colormod = '0 0 0';
561         else if (c ==  0) self.colormod = '1.00 1.00 1.00';
562         else if (c ==  3) self.colormod = '0.10 1.73 0.10';
563         else if (c ==  4) self.colormod = '1.73 0.10 0.10';
564         else if (c == 12) self.colormod = '1.22 1.22 0.10';
565         else if (c == 13) self.colormod = '0.10 0.10 1.73';
566         else self.colormod = '1 1 1';
567 };*/
568
569 void UpdatePlayerColors () {
570         if(self.weaponentity) {
571                 self.weaponentity.colormap = self.colormap;
572                 self.exteriorweaponentity.colormap = self.colormap;
573         }
574 }
575 /*
576 =============
577 PlayerJump
578
579 When you press the jump key
580 =============
581 */
582 void PlayerJump (void)
583 {
584         float mjumpheight;
585
586         mjumpheight = cvar("g_balance_jumpheight");
587         if (self.waterlevel >= 2)
588         {
589                 if (self.watertype == CONTENT_WATER)
590                         self.velocity_z = 200;
591                 else if (self.watertype == CONTENT_SLIME)
592                         self.velocity_z = 80;
593                 else
594                         self.velocity_z = 50;
595
596                 return;
597         }
598
599
600         if (!(self.flags & FL_ONGROUND))
601                 return;
602
603         if (!(self.flags & FL_JUMPRELEASED))
604                 return;
605
606         if(cvar("g_runematch"))
607         {
608                 if(self.runes & RUNE_SPEED)
609                 {
610                         if(self.runes & CURSE_SLOW)
611                                 mjumpheight = mjumpheight * cvar("g_balance_rune_speed_combo_jumpheight");
612                         else
613                                 mjumpheight = mjumpheight * cvar("g_balance_rune_speed_jumpheight");
614                 }
615                 else if(self.runes & CURSE_SLOW)
616                 {
617                         mjumpheight = mjumpheight * cvar("g_balance_curse_slow_jumpheight");
618                 }
619         }
620
621         if(cvar("g_minstagib") && (self.items & IT_INVINCIBLE))
622         {
623                 mjumpheight = mjumpheight * cvar("g_balance_rune_speed_jumpheight");
624         }
625
626         self.velocity_z = self.velocity_z + mjumpheight;
627         self.oldvelocity_z = self.velocity_z;
628
629         self.flags = self.flags - FL_ONGROUND;
630         self.flags = self.flags - FL_JUMPRELEASED;
631 }
632
633 void() CheckWaterJump =
634 {
635         local vector start, end;
636
637 // check for a jump-out-of-water
638         makevectors (self.angles);
639         start = self.origin;
640         start_z = start_z + 8;
641         v_forward_z = 0;
642         normalize(v_forward);
643         end = start + v_forward*24;
644         traceline (start, end, TRUE, self);
645         if (trace_fraction < 1)
646         {       // solid at waist
647                 start_z = start_z + self.maxs_z - 8;
648                 end = start + v_forward*24;
649                 self.movedir = trace_plane_normal * -50;
650                 traceline (start, end, TRUE, self);
651                 if (trace_fraction == 1)
652                 {       // open at eye level
653                         self.flags = self.flags | FL_WATERJUMP;
654                         self.velocity_z = 225;
655                         self.flags = self.flags - (self.flags & FL_JUMPRELEASED);
656                         self.teleport_time = time + 2;  // safety net
657                         return;
658                 }
659         }
660 };
661
662
663 void respawn(void)
664 {
665         CopyBody(1);
666         PutClientInServer();
667 }
668
669 void player_powerups (void)
670 {
671         if (cvar("g_minstagib"))
672         {
673                 self.effects = EF_FULLBRIGHT;
674                 if (self.items & IT_STRENGTH)
675                 {
676                         self.effects = EF_NODRAW;
677                         if (time > self.strength_finished)
678                         {
679                                 self.items = self.items - (self.items & IT_STRENGTH);
680                                 stuffcmd(self, strcat("crosshair_static ", self.crosshair_static, "\n"));
681                                 sprint(self, "^3Invisibility has worn off\n");
682                         }
683                 }
684                 else
685                 {
686                         if (time < self.strength_finished)
687                         {
688                                 stuffcmd(self, "crosshair_static 1\n");
689                                 self.items = self.items | IT_STRENGTH;
690                                 sprint(self, "^3You are invisible\n");
691                         }
692                 }
693                                 
694                 if (self.items & IT_INVINCIBLE)
695                 {
696                         if (time > self.invincible_finished)
697                         {
698                                 self.items = self.items - (self.items & IT_INVINCIBLE);
699                                 //stuffcmd(self, strcat("crosshair_static ", ftos(self.crosshair_static), "\n"));
700                                 sprint(self, "^3Speed has worn off\n");
701                         }
702                 }
703                 else
704                 {
705                         if (time < self.invincible_finished)
706                         {
707                                 stuffcmd(self, "crosshair_static 1\n");
708                                 self.items = self.items | IT_INVINCIBLE;
709                                 sprint(self, "^3You are on speed\n");
710                         }
711                 }
712                 return;
713         }
714         
715         self.effects = self.effects - (self.effects & (EF_RED | EF_BLUE | EF_ADDITIVE | EF_FULLBRIGHT));
716         if (self.items & IT_STRENGTH)
717         {
718                 self.effects = self.effects | (EF_BLUE | EF_ADDITIVE | EF_FULLBRIGHT);
719                 if (time > self.strength_finished)
720                 {
721                         self.items = self.items - (self.items & IT_STRENGTH);
722                         sprint(self, "^3Strength has worn off\n");
723                 }
724         }
725         else
726         {
727                 if (time < self.strength_finished)
728                 {
729                         self.items = self.items | IT_STRENGTH;
730                         sprint(self, "^3Strength infuses your weapons with devestating power\n");
731                 }
732         }
733         if (self.items & IT_INVINCIBLE)
734         {
735                 self.effects = self.effects | (EF_RED | EF_ADDITIVE | EF_FULLBRIGHT);
736                 if (time > self.invincible_finished)
737                 {
738                         self.items = self.items - (self.items & IT_INVINCIBLE);
739                         sprint(self, "^3Shield has worn off\n");
740                 }
741         }
742         else
743         {
744                 if (time < self.invincible_finished)
745                 {
746                         self.items = self.items | IT_INVINCIBLE;
747                         sprint(self, "^3Shield surrounds you\n");
748                 }
749         }
750         
751         if (cvar("g_fullbrightplayers"))
752                 self.effects = EF_FULLBRIGHT;
753         
754 }
755
756 void player_regen (void)
757 {
758         float maxh, maxa, max_mod, regen_mod, rot_mod;
759         maxh = cvar("g_balance_health_stable");
760         maxa = cvar("g_balance_armor_stable");
761
762         if (cvar("g_minstagib"))
763         {
764                 maxh = 100;
765                 maxa = 0;
766                 return;
767         }
768
769         if(cvar("g_runematch"))
770         {
771                 max_mod = regen_mod = rot_mod = 1;
772                 if (self.runes & RUNE_REGEN)
773                 {
774                         if (self.runes & CURSE_VENOM) // do we have both rune/curse?
775                         {
776                                 regen_mod = cvar("g_balance_rune_regen_combo_regenrate");
777                                 max_mod = cvar("g_balance_rune_regen_combo_hpmod");
778                         }
779                         else
780                         {
781                                 regen_mod = cvar("g_balance_rune_regen_regenrate");
782                                 max_mod = cvar("g_balance_rune_regen_hpmod");
783                         }
784                 }
785                 else if (self.runes & CURSE_VENOM)
786                 {
787                         max_mod = cvar("g_balance_curse_venom_hpmod");
788                         if (self.runes & RUNE_REGEN) // do we have both rune/curse?
789                                 rot_mod = cvar("g_balance_rune_regen_combo_rotrate");
790                         else
791                                 rot_mod = cvar("g_balance_curse_venom_rotrate");
792                         //if (!self.runes & RUNE_REGEN)
793                         //      rot_mod = cvar("g_balance_curse_venom_rotrate");
794                 }
795                 maxh = maxh * max_mod;
796                 //maxa = maxa * max_mod;
797
798                 if (time > self.pauserotarmor_finished)
799                 {
800                         if (self.armorvalue > maxa)
801                                 self.armorvalue = bound(0, self.armorvalue + (maxa - self.armorvalue) * cvar("g_balance_armor_rot") * frametime, 1000);
802                 }
803                 if (time > self.pauserothealth_finished)
804                 {
805                         if (self.health > maxh)
806                                 self.health = bound(0, self.health + (maxh - self.health) * rot_mod*cvar("g_balance_health_rot") * frametime, 1000);
807                 }
808                 if (time > self.pauseregen_finished)
809                 {
810                         if (self.health < maxh)
811                                 self.health = bound(0, self.health + (maxh- self.health) * regen_mod*cvar("g_balance_health_regen") * frametime, 1000);
812                         if (self.armorvalue < maxa)
813                                 self.armorvalue = bound(0, self.armorvalue + (maxa - self.armorvalue) * cvar("g_balance_armor_regen") * frametime, 1000);
814                 }
815         }
816         else
817         {
818                 if (time > self.pauserothealth_finished)
819                 if (self.health > maxh)
820                         self.health = bound(0, self.health + (maxh - self.health) * cvar("g_balance_health_rot") * frametime, 1000);
821                 if (time > self.pauserotarmor_finished)
822                 if (self.armorvalue > maxa)
823                         self.armorvalue = bound(0, self.armorvalue + (maxa - self.armorvalue) * cvar("g_balance_armor_rot") * frametime, 1000);
824                 if (time > self.pauseregen_finished)
825                 {
826                         if (self.health < maxh)
827                                 self.health = bound(0, self.health + (maxh- self.health) * cvar("g_balance_health_regen") * frametime, 1000);
828                         if (self.armorvalue < maxa)
829                                 self.armorvalue = bound(0, self.armorvalue + (maxa - self.armorvalue) * cvar("g_balance_armor_regen") * frametime, 1000);
830                 }
831         }
832 }
833
834 /*
835 ======================
836 spectate mode routines
837 ======================
838 */
839 void SpectateCopy(entity spectatee) {
840         self.armortype = spectatee.armortype;
841         self.armorvalue = spectatee.armorvalue;
842         self.currentammo = spectatee.currentammo;
843         self.effects = spectatee.effects;
844         self.health = spectatee.health;
845         self.impulse = 0;
846         self.items = spectatee.items;
847         self.punchangle = spectatee.punchangle;
848         self.view_ofs = spectatee.view_ofs;
849         self.v_angle = spectatee.v_angle;
850         self.viewzoom = spectatee.viewzoom;
851         setorigin(self, spectatee.origin);
852         setsize(self, spectatee.mins, spectatee.maxs);
853 }
854
855 void SpectateUpdate() {
856         if (self != self.enemy) {
857                 SpectateCopy(self.enemy);
858                 msg_entity = self;
859                 WriteByte(MSG_ONE, SVC_SETANGLE);
860                 WriteAngle(MSG_ONE, self.enemy.v_angle_x);
861                 WriteAngle(MSG_ONE, self.enemy.v_angle_y);
862                 WriteAngle(MSG_ONE, self.enemy.v_angle_z);
863         }
864 }
865
866 float SpectateNext() {
867         other = find(self.enemy, classname, "player");
868         if (!other) {
869                 other = find(other, classname, "player");
870         }
871         if (other) {
872                 self.enemy = other;
873         }
874         if(self.enemy.classname == "player") {
875                 msg_entity = self;
876                 WriteByte(MSG_ONE, SVC_SETVIEW);
877                 WriteEntity(MSG_ONE, self.enemy);
878                 //stuffcmd(self, "set viewsize $tmpviewsize \n");
879                 SpectateUpdate();
880                 return 1;
881         } else {
882                 return 0;
883         }
884 }
885
886 /*
887 =============
888 PlayerPreThink
889
890 Called every frame for each client before the physics are run
891 =============
892 */
893 void PlayerPreThink (void)
894 {
895         if(self.classname == "player") {
896                 local vector m1, m2;
897
898 //              MauveBot_AI();
899
900 //              if(self.netname == "Wazat")
901 //                      bprint(strcat(self.classname, "\n"));
902
903                 CheckRules_Player();
904
905                 if (intermission_running)
906                 {
907                         IntermissionThink ();   // otherwise a button could be missed between
908                         return;                                 // the think tics
909                 }
910
911                 if (self.deadflag != DEAD_NO)
912                 {
913                         player_anim();
914                         weapon_freeze();
915                         if (self.deadflag == DEAD_DYING)
916                         {
917                                 if (time > self.dead_time)
918                                         self.deadflag = DEAD_DEAD;
919                         }
920                         else if (self.deadflag == DEAD_DEAD)
921                         {
922                                 if (!self.button0 && !self.button2 && !self.button3)
923                                         self.deadflag = DEAD_RESPAWNABLE;
924                         }
925                         else if (self.deadflag == DEAD_RESPAWNABLE)
926                         {
927                                 if (self.button0 || self.button2 || self.button3  || self.button4)
928                                         respawn();
929                         }
930                         return;
931                 }
932
933                 if (self.button5)
934                 {
935                         if (!self.crouch)
936                         {
937                                 self.crouch = TRUE;
938                                 self.view_ofs = PL_CROUCH_VIEW_OFS;
939                                 setsize (self, PL_CROUCH_MIN, PL_CROUCH_MAX);
940                         }
941                 }
942                 else
943                 {
944                         if (self.crouch)
945                         {
946                                 tracebox(self.origin, PL_MIN, PL_MAX, self.origin, FALSE, self);
947                                 if (!trace_startsolid)
948                                 {
949                                         self.crouch = FALSE;
950                                         self.view_ofs = PL_VIEW_OFS;
951                                         setsize (self, PL_MIN, PL_MAX);
952                                 }
953                         }
954                 }
955
956                 if(cvar("sv_defaultcharacter") == 1) {
957                         local string defaultmodel;
958                         defaultmodel = CheckPlayerModel(cvar_string("sv_defaultplayermodel"));
959                         
960                         if (defaultmodel != self.model)
961                         {
962                                 m1 = self.mins;
963                                 m2 = self.maxs;
964                                 precache_model (defaultmodel);
965                                 setmodel (self, defaultmodel);
966                                 setsize (self, m1, m2);
967                         }
968
969                         if (self.skin != stof(cvar_string("sv_defaultplayerskin")))
970                                 self.skin = stof(cvar_string("sv_defaultplayerskin"));
971                 } else {
972                         if (self.playermodel != self.model)
973                         {
974                                 self.playermodel = CheckPlayerModel(self.playermodel);
975                                 m1 = self.mins;
976                                 m2 = self.maxs;
977                                 precache_model (self.playermodel);
978                                 setmodel (self, self.playermodel);
979                                 setsize (self, m1, m2);
980                         }
981
982                         if (self.skin != stof(self.playerskin))
983                                 self.skin = stof(self.playerskin);
984                 }
985                 // Savage: Check for nameless players
986                 if (strlen(self.netname) < 1) {
987                         self.netname = "Player";
988                         stuffcmd(self, "name Player\n");
989                 }
990         
991                 GrapplingHookFrame();
992
993                 W_WeaponFrame();
994
995                 if (self.button4 || (self.weapon == WEP_NEX && self.button3))
996                 {
997                         if (cvar("g_minstagib") && self.button3)
998                         {
999                                 if (self.jump_interval <= (time + 0.1))
1000                                 {
1001                                         self.jump_interval = time + 1;
1002                                         weapon_doattack(laser_check, laser_check, W_Laser_Attack);
1003                                 }
1004                         }
1005                         else if (self.viewzoom > 0.4)
1006                                 self.viewzoom = max (0.4, self.viewzoom - frametime * 2);
1007                 }
1008                 else if (self.viewzoom < 1.0)
1009                         self.viewzoom = min (1.0, self.viewzoom + frametime);
1010
1011
1012                 if (self.button2)
1013                         PlayerJump ();
1014                 else
1015                         self.flags = self.flags | FL_JUMPRELEASED;
1016
1017                 if (self.vote_finished > 0 // this player has called a vote
1018                     && time > self.vote_finished) // time is up
1019                 {
1020                         VoteCount();
1021                 }
1022
1023                 player_powerups();
1024                 player_regen();
1025                 player_anim();
1026
1027                 //self.angles_y=self.v_angle_y + 90;   // temp
1028
1029                 if (self.waterlevel == 2)
1030                         CheckWaterJump ();
1031
1032                 //if (TetrisPreFrame()) return;
1033         } else if(self.classname == "observer") {
1034                 if (self.flags & FL_JUMPRELEASED) {                                                                                                                           
1035                         if (self.button2) {
1036                                 self.flags = self.flags & !FL_JUMPRELEASED;
1037                                 self.classname = "player";
1038                                 bprint (strcat("^4", self.netname, "^4 is playing now\n"));
1039                                 PutClientInServer();
1040                                 centerprint(self,"");
1041                                 return;
1042                         } else if(self.button0) {
1043                                 self.flags = self.flags & !FL_JUMPRELEASED;
1044                                 if(SpectateNext() == 1) {
1045                                         self.classname = "spectator";
1046                                 } 
1047                         }
1048         } else {
1049                 if (!(self.button0 || self.button2)) {
1050                                 self.flags = self.flags | FL_JUMPRELEASED;
1051                 }
1052                 }
1053                 centerprint(self, strcat("\n", "\n", "\n", "press jump to play", "\n", "press attack to spectate other players"));
1054         } else if(self.classname == "spectator") {
1055                 if (self.flags & FL_JUMPRELEASED) {
1056                         if(self.button0) {
1057                                 self.flags = self.flags & !FL_JUMPRELEASED;
1058                                 if(SpectateNext() == 1) {
1059                                         self.classname = "spectator";
1060                                 } else {
1061                                         self.classname = "observer";
1062                                         msg_entity = self;                                                                                                                                                     
1063                                         WriteByte(MSG_ONE, SVC_SETVIEW);                                                                                                                                       
1064                                         WriteEntity(MSG_ONE, self);   
1065                                         PutClientInServer();                                    
1066                                 }
1067                         } else if (self.button3) {
1068                                 self.flags = self.flags & !FL_JUMPRELEASED;
1069                                 self.classname = "observer";
1070                                 msg_entity = self;                                                                                                                                                     
1071                                 WriteByte(MSG_ONE, SVC_SETVIEW);                                                                                                                                       
1072                                 WriteEntity(MSG_ONE, self);   
1073                                 PutClientInServer();
1074                         } else {
1075                                 SpectateUpdate();
1076                         }
1077         } else {
1078                 if (!(self.button0 || self.button3)) {
1079                                 self.flags = self.flags | FL_JUMPRELEASED;
1080                 }
1081                 }
1082                 centerprint(self, strcat("spectating ", self.enemy.netname, "\n", "\n", "\n", "press attack for next player", "\n", "press attack2 for free fly mode"));
1083         }
1084         
1085 }
1086
1087 /*
1088 =============
1089 PlayerPostThink
1090
1091 Called every frame for each client after the physics are run
1092 =============
1093 */
1094 void PlayerPostThink (void)
1095 {
1096         if(self.classname == "player") {
1097                 CheckRules_Player();
1098                 UpdateChatBubble();
1099                 UpdatePlayerColors();
1100                 if (self.deadflag == DEAD_NO)
1101                 if (self.impulse)
1102                         ImpulseCommands ();
1103                 if (intermission_running)
1104                         return;         // intermission or finale
1105
1106                 PrintWelcomeMessage(self);
1107                 //if (TetrisPostFrame()) return;
1108         } else if (self.classname == "observer") {
1109                 //do nothing
1110         } else if (self.classname == "spectator") {
1111                 //do nothing
1112         }
1113 }