]> icculus.org git repositories - divverent/nexuiz.git/blob - data/qcsrc/gamec/cl_client.c
fixed some bugs with scoretransfer on teamchange
[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         if(self.frags == 0 && cvar("g_lms"))
167                 bprint (strcat("^4", self.netname, "^4 has no more lives left\n"));
168         else if(self.killcount != -666)
169                 bprint (strcat("^4", self.netname, "^4 is spectating now\n"));
170
171         self.classname = "observer";
172         self.health = 666;
173         self.takedamage = DAMAGE_NO;
174         self.solid = SOLID_NOT;
175         self.movetype = MOVETYPE_NOCLIP;
176         self.armorvalue = 666;
177         self.effects = 0;
178         self.armorvalue = cvar("g_balance_armor_start");
179         self.pauserotarmor_finished = 0;
180         self.pauserothealth_finished = 0;
181         self.pauseregen_finished = 0;
182         self.damageforcescale = 0;
183         self.death_time = 0;
184         self.dead_time = 0;
185         self.dead_frame = 0;
186         self.die_frame = 0;
187         self.alpha = 0;
188         self.scale = 0;
189         self.fade_time = 0;
190         self.pain_frame = 0;
191         self.pain_finished = 0;
192         self.strength_finished = 0;
193         self.invincible_finished = 0;
194         self.pushltime = 0;
195         self.vote_finished = 0;
196         self.think = SUB_Null;
197         self.nextthink = 0;
198         self.hook_time = 0;
199         self.runes = 0;
200         self.deadflag = DEAD_NO;
201         self.angles = spot.angles;
202         self.angles_z = 0; 
203         self.fixangle = TRUE; 
204         self.crouch = FALSE;
205         self.view_ofs = PL_VIEW_OFS;
206         setorigin (self, spot.origin + '0 0 1' * (1 - self.mins_z - 24));
207         self.oldorigin = self.origin;
208         self.items = 0;
209         self.model = "";
210         self.modelindex = 0;
211         self.weapon = 0;
212         self.weaponmodel = "";
213         self.weaponframe = 0;
214         self.weaponentity = world;
215         self.killcount = -666;
216         if(!cvar("g_lms"))
217                 self.frags = -666;
218         //stuffcmd(self, "set viewsize 120 \n");
219 //      bprint (strcat("^4", self.netname, "^4 is spectating now\n"));
220 }
221
222
223 /*
224 =============
225 PutClientInServer
226
227 Called when a client spawns in the server
228 =============
229 */
230 void PutClientInServer (void)
231 {       
232         if(clienttype(self) ==  CLIENTTYPE_BOT)
233         {
234                 if(cvar("g_lms"))
235                 {
236                         if(self.frags < 0)
237                         {
238                                 self.classname = "player";
239                         }
240                 }
241                 else
242                 {
243                         self.classname = "player";
244                 }
245         }
246         
247         // player is dead and becomes observer
248         if(cvar("g_lms") && self.frags < 1)
249                 self.classname = "observer";
250
251         if(self.classname == "player") {
252                 entity  spot;
253
254                 spot = SelectSpawnPoint (FALSE);
255                 
256                 RemoveGrapplingHook(self); // Wazat's Grappling Hook
257         
258                 self.classname = "player";
259                 self.iscreature = TRUE;
260                 self.movetype = MOVETYPE_WALK;
261                 self.solid = SOLID_SLIDEBOX;
262                 self.flags = FL_CLIENT;
263                 self.takedamage = DAMAGE_AIM;
264                 self.effects = 0;
265                 self.health = cvar("g_balance_health_start");
266                 self.armorvalue = cvar("g_balance_armor_start");
267                 self.pauserotarmor_finished = time + 10;
268                 self.pauserothealth_finished = time + 10;
269                 self.pauseregen_finished = 0;
270                 self.damageforcescale = 2;
271                 self.death_time = 0;
272                 self.dead_time = 0;
273                 self.dead_frame = 0;
274                 self.die_frame = 0;
275                 self.alpha = 0;
276                 self.scale = 0;
277                 self.fade_time = 0;
278                 self.pain_frame = 0;
279                 self.pain_finished = 0;
280                 self.strength_finished = 0;
281                 self.invincible_finished = 0;
282                 self.pushltime = 0;
283                 //self.speed_finished = 0;
284                 //self.slowmo_finished = 0;
285                 self.vote_finished = 0;
286                 // players have no think function
287                 self.think = SUB_Null;
288                 self.nextthink = 0;
289
290                 self.hook_time = 0;
291         
292                 self.runes = 0;
293
294                 self.deadflag = DEAD_NO;
295
296                 self.angles = spot.angles;
297
298                 self.angles_z = 0; // never spawn tilted even if the spot says to
299                 self.fixangle = TRUE; // turn this way immediately
300                 self.velocity = '0 0 0';
301                 self.avelocity = '0 0 0';
302                 self.punchangle = '0 0 0';
303                 self.punchvector = '0 0 0';
304                 self.oldvelocity = self.velocity;
305         
306                 self.viewzoom = 0.6;
307
308                 if(cvar("sv_defaultcharacter") == 1) {
309                         local string defaultmodel;
310                         defaultmodel = CheckPlayerModel(cvar_string("sv_defaultplayermodel"));
311
312                         precache_model (defaultmodel);
313                         setmodel (self, defaultmodel);
314                         self.skin = stof(cvar_string("sv_defaultplayerskin"));
315                 } else {
316                         self.playermodel = CheckPlayerModel(self.playermodel);
317
318                         precache_model (self.playermodel);
319                         setmodel (self, self.playermodel);
320                         self.skin = stof(self.playerskin);
321                 
322                 }
323                 
324                 self.crouch = FALSE;
325                 self.view_ofs = PL_VIEW_OFS;
326                 setsize (self, PL_MIN, PL_MAX);
327                 setorigin (self, spot.origin + '0 0 1' * (1 - self.mins_z - 24));
328                 // don't reset back to last position, even if new position is stuck in solid
329                 self.oldorigin = self.origin;
330
331                 if(cvar("g_lms"))
332                 {
333                         self.ammo_shells = cvar("g_lms_start_ammo_shells");
334                         self.ammo_nails = cvar("g_lms_start_ammo_nails");
335                         self.ammo_rockets = cvar("g_lms_start_ammo_rockets");
336                         self.ammo_cells = cvar("g_lms_start_ammo_cells");
337                 }
338                 else if (cvar("g_use_ammunition")) {
339                         self.ammo_shells = cvar("g_start_ammo_shells");
340                         self.ammo_nails = cvar("g_start_ammo_nails");
341                         self.ammo_rockets = cvar("g_start_ammo_rockets");
342                         self.ammo_cells = cvar("g_start_ammo_cells");
343                 } else {
344                         self.ammo_shells = 999;
345                         self.ammo_nails = 999;
346                         self.ammo_rockets = 999;
347                         self.ammo_cells = 999;
348                 }
349
350                 self.items = 0;
351                 if (cvar("g_start_weapon_laser") || cvar("g_lms"))
352                 {
353                         self.items = self.items | IT_LASER;
354                         self.switchweapon = WEP_LASER;
355                 }
356                 if (cvar("g_start_weapon_shotgun") || cvar("g_lms"))
357                 {
358                         self.items = self.items | IT_SHOTGUN;
359                         self.switchweapon = WEP_SHOTGUN;
360                 }
361                 if (cvar("g_start_weapon_uzi") || cvar("g_lms"))
362                 {
363                         self.items = self.items | IT_UZI;
364                         self.switchweapon = WEP_UZI;
365                 }
366                 if (cvar("g_start_weapon_grenadelauncher") || cvar("g_lms"))
367                 {
368                         self.items = self.items | IT_GRENADE_LAUNCHER;
369                         self.switchweapon = WEP_GRENADE_LAUNCHER;
370                 }
371                 if (cvar("g_start_weapon_electro") || cvar("g_lms"))
372                 {
373                         self.items = self.items | IT_ELECTRO;
374                         self.switchweapon = WEP_ELECTRO;
375                 }
376                 if (cvar("g_start_weapon_crylink") || cvar("g_lms"))
377                 {
378                         self.items = self.items | IT_CRYLINK;
379                         self.switchweapon = WEP_CRYLINK;
380                 }
381                 if (cvar("g_start_weapon_nex") || cvar("g_lms"))
382                 {
383                         self.items = self.items | IT_NEX;
384                         self.switchweapon = WEP_NEX;
385                 }
386                 if (cvar("g_start_weapon_hagar") || cvar("g_lms"))
387                 {
388                         self.items = self.items | IT_HAGAR;
389                         self.switchweapon = WEP_HAGAR;
390                 }
391                 if (cvar("g_start_weapon_rocketlauncher") || cvar("g_lms"))
392                 {
393                         self.items = self.items | IT_ROCKET_LAUNCHER;
394                         self.switchweapon = WEP_ROCKET_LAUNCHER;
395                 }
396
397                 if(cvar("g_instagib"))
398                 {
399                         self.items = IT_NEX;
400                         self.switchweapon = WEP_NEX;
401                         self.ammo_cells = 999;
402                 }
403
404                 if(cvar("g_rocketarena"))
405                 {
406                         self.items = IT_ROCKET_LAUNCHER;
407                         self.switchweapon = WEP_ROCKET_LAUNCHER;
408                         self.ammo_rockets = 999;
409                 }
410
411                 if(cvar("g_minstagib"))
412                 {
413                         self.health = 100;
414                         self.armorvalue = 0;
415                         self.items = IT_NEX;
416                         self.switchweapon = WEP_NEX;
417                         self.ammo_cells = cvar("g_minstagib_ammo_start");
418                         self.extralives = 0;
419                         self.jump_interval = time;
420                 }
421         
422                 self.event_damage = PlayerDamage;
423         
424                 self.statdraintime = time + 5;
425                 self.button0 = self.button1 = self.button2 = self.button3 = 0;
426         
427                 if(self.frags == -666 && self.killcount == -666) {
428                         self.killcount = 0;
429                         self.frags = 0;
430                 }
431         
432                 /*
433                 W_UpdateWeapon();
434                 W_UpdateAmmo();
435                 */
436                 CL_SpawnWeaponentity();
437         
438                 //stuffcmd(self, "chase_active 0");
439                 //stuffcmd(self, "set viewsize $tmpviewsize \n");
440         } else if(self.classname == "observer") {
441                 PutObserverInServer ();
442         }
443 }
444
445 /*
446 =============
447 SetNewParms
448 =============
449 */
450 void SetNewParms (void)
451 {
452
453 }
454
455 /*
456 =============
457 SetChangeParms
458 =============
459 */
460 void SetChangeParms (void)
461 {
462
463 }
464
465 /*
466 =============
467 ClientKill
468
469 Called when a client types 'kill' in the console
470 =============
471 */
472 void ClientKill (void)
473 {
474         Damage(self, self, self, 100000, DEATH_KILL, self.origin, '0 0 0');
475 }
476
477 /*
478 =============
479 ClientConnect
480
481 Called when a client connects to the server
482 =============
483 */
484 string ColoredTeamName(float t);
485 //void dom_player_join_team(entity pl);
486 void ClientConnect (void)
487 {
488         self.classname = "player_joining";
489
490         //if(cvar("g_domination"))
491         //      dom_player_join_team(self);
492
493         //JoinBestTeam(self, FALSE);
494         if(cvar("teamplay")) stuffcmd(self,"menu_showteamselect\n");
495         
496
497         if(cvar("sv_spectate") == 1 && !cvar("g_lms")) {
498                 self.classname = "observer";    
499         } else {
500                 self.classname = "player";              
501         }
502         
503         //stuffcmd(self, "set tmpviewsize $viewsize \n");
504         
505         bprint ("^4",self.netname);
506         bprint ("^4 connected");
507
508         if(cvar("g_domination") || cvar("g_ctf"))
509         {
510                 bprint(" and joined the ");
511                 bprint(ColoredTeamName(self.team));
512         }
513
514         bprint("\n");
515
516         self.welcomemessage_time = time + cvar("welcome_message_time");
517         self.welcomemessage_time2 = 0;
518
519         stuffcmd(self, strcat("exec maps/", mapname, ".cfg\n"));
520         // send prediction settings to the client
521         stuffcmd(self, strcat("cl_movement_maxspeed ", ftos(cvar("sv_maxspeed")), "\n"));
522         stuffcmd(self, strcat("cl_movement_maxairspeed ", ftos(cvar("sv_maxairspeed")), "\n"));
523         stuffcmd(self, strcat("cl_movement_accelerate ", ftos(cvar("sv_accelerate")), "\n"));
524         stuffcmd(self, strcat("cl_movement_friction ", ftos(cvar("sv_friction")), "\n"));
525         stuffcmd(self, strcat("cl_movement_stopspeed ", ftos(cvar("sv_stopspeed")), "\n"));
526         stuffcmd(self, strcat("cl_movement_jumpvelocity ", ftos(cvar("g_balance_jumpheight")), "\n"));
527         stuffcmd(self, strcat("cl_movement_stepheight ", ftos(cvar("sv_stepheight")), "\n"));
528         stuffcmd(self, strcat("cl_movement_edgefriction 0\n"));
529         // Wazat's grappling hook
530         SetGrappleHookBindings();       
531
532         // get autoswitch state from player
533         stuffcmd(self, "alias autoswitch \"set cl_autoswitch $1; cmd autoswitch $1\"\n");
534         stuffcmd(self, "cmd autoswitch $cl_autoswitch\n");
535
536         // get version info from player
537         stuffcmd(self, "cmd clientversion $g_nexuizversion_major\n");
538
539         // set cvar for team scoreboard
540         if(teams_matter)
541                 stuffcmd(self, "set teamplay 1\n");
542         else
543                 stuffcmd(self, "set teamplay 0\n");
544
545         if(cvar("g_lms"))
546         {
547                 self.frags = cvar("fraglimit");
548                 // no fraglimit was set, so player gets 999 lives
549                 if(self.frags < 1)
550                         self.frags = 999;
551
552                 // disallow player to join after the worst player has lost g_lms_last_join lives
553                 // if "g_lms_join_anytime" new players spawn with same amount of lives as the worst active player
554                 if((cvar("fraglimit") - cvar("g_lms_last_join")) > lms_lowest_lives && !cvar("g_lms_join_anytime"))
555                 {
556                         self.frags = -1;
557                         lms_dead_count += 1;
558                 }
559                 else if(cvar("fraglimit") > lms_lowest_lives)
560                 {
561                         self.frags = lms_lowest_lives;
562                 }
563         }
564
565         if(clienttype(self) !=  CLIENTTYPE_BOT)
566                 player_count += 1;
567 }
568
569 /*
570 =============
571 ClientDisconnect
572
573 Called when a client disconnects from the server
574 =============
575 */
576 .entity chatbubbleentity;
577 void ClientDisconnect (void)
578 {
579         bprint ("^4",self.netname);
580         bprint ("^4 disconnected\n");
581
582         if (self.chatbubbleentity)
583         {
584                 remove (self.chatbubbleentity);
585                 self.chatbubbleentity = world;
586         }
587         DropAllRunes(self);
588         // decrease player count for lms
589         if(clienttype(self) !=  CLIENTTYPE_BOT)
590                 player_count -= 1;
591         // player was dead, decrease dead count
592         if(cvar("g_lms") && self.frags < 1)
593                 lms_dead_count -= 1;
594         //stuffcmd(self, "set viewsize $tmpviewsize \n");
595 }
596
597 .float buttonchat;
598 void() ChatBubbleThink =
599 {
600         self.nextthink = time;
601         if (!self.owner.modelindex || self.owner.chatbubbleentity != self)
602         {
603                 remove(self);
604                 return;
605         }
606         setorigin(self, self.owner.origin + '0 0 15' + self.owner.maxs_z * '0 0 1');
607         if (self.owner.buttonchat && !self.owner.deadflag)
608                 self.model = self.mdl;
609         else
610                 self.model = "";
611 };
612
613 void() UpdateChatBubble =
614 {
615         if (!self.modelindex)
616                 return;
617         // spawn a chatbubble entity if needed
618         if (!self.chatbubbleentity)
619         {
620                 self.chatbubbleentity = spawn();
621                 self.chatbubbleentity.owner = self;
622                 self.chatbubbleentity.exteriormodeltoclient = self;
623                 self.chatbubbleentity.think = ChatBubbleThink;
624                 self.chatbubbleentity.nextthink = time;
625                 setmodel(self.chatbubbleentity, "models/misc/chatbubble.spr");
626                 setorigin(self.chatbubbleentity, self.origin + '0 0 15' + self.maxs_z * '0 0 1');
627                 self.chatbubbleentity.mdl = self.chatbubbleentity.model;
628                 self.chatbubbleentity.model = "";
629         }
630 }
631
632 // LordHavoc: this hack will be removed when proper _pants/_shirt layers are
633 // added to the model skins
634 /*void() UpdateColorModHack =
635 {
636         local float c;
637         c = self.clientcolors & 15;
638         // LordHavoc: only bothering to support white, green, red, yellow, blue
639              if (teamplay == 0) self.colormod = '0 0 0';
640         else if (c ==  0) self.colormod = '1.00 1.00 1.00';
641         else if (c ==  3) self.colormod = '0.10 1.73 0.10';
642         else if (c ==  4) self.colormod = '1.73 0.10 0.10';
643         else if (c == 12) self.colormod = '1.22 1.22 0.10';
644         else if (c == 13) self.colormod = '0.10 0.10 1.73';
645         else self.colormod = '1 1 1';
646 };*/
647
648 void UpdatePlayerColors () {
649         if(self.weaponentity) {
650                 self.weaponentity.colormap = self.colormap;
651                 self.exteriorweaponentity.colormap = self.colormap;
652         }
653 }
654 /*
655 =============
656 PlayerJump
657
658 When you press the jump key
659 =============
660 */
661 void PlayerJump (void)
662 {
663         float mjumpheight;
664
665         mjumpheight = cvar("g_balance_jumpheight");
666         if (self.waterlevel >= 2)
667         {
668                 if (self.watertype == CONTENT_WATER)
669                         self.velocity_z = 200;
670                 else if (self.watertype == CONTENT_SLIME)
671                         self.velocity_z = 80;
672                 else
673                         self.velocity_z = 50;
674
675                 return;
676         }
677
678
679         if (!(self.flags & FL_ONGROUND))
680                 return;
681
682         if (!(self.flags & FL_JUMPRELEASED))
683                 return;
684
685         if(cvar("g_runematch"))
686         {
687                 if(self.runes & RUNE_SPEED)
688                 {
689                         if(self.runes & CURSE_SLOW)
690                                 mjumpheight = mjumpheight * cvar("g_balance_rune_speed_combo_jumpheight");
691                         else
692                                 mjumpheight = mjumpheight * cvar("g_balance_rune_speed_jumpheight");
693                 }
694                 else if(self.runes & CURSE_SLOW)
695                 {
696                         mjumpheight = mjumpheight * cvar("g_balance_curse_slow_jumpheight");
697                 }
698         }
699
700         if(cvar("g_minstagib") && (self.items & IT_INVINCIBLE))
701         {
702                 mjumpheight = mjumpheight * cvar("g_balance_rune_speed_jumpheight");
703         }
704
705         self.velocity_z = self.velocity_z + mjumpheight;
706         self.oldvelocity_z = self.velocity_z;
707
708         self.flags = self.flags - FL_ONGROUND;
709         self.flags = self.flags - FL_JUMPRELEASED;
710 }
711
712 void() CheckWaterJump =
713 {
714         local vector start, end;
715
716 // check for a jump-out-of-water
717         makevectors (self.angles);
718         start = self.origin;
719         start_z = start_z + 8;
720         v_forward_z = 0;
721         normalize(v_forward);
722         end = start + v_forward*24;
723         traceline (start, end, TRUE, self);
724         if (trace_fraction < 1)
725         {       // solid at waist
726                 start_z = start_z + self.maxs_z - 8;
727                 end = start + v_forward*24;
728                 self.movedir = trace_plane_normal * -50;
729                 traceline (start, end, TRUE, self);
730                 if (trace_fraction == 1)
731                 {       // open at eye level
732                         self.flags = self.flags | FL_WATERJUMP;
733                         self.velocity_z = 225;
734                         self.flags = self.flags - (self.flags & FL_JUMPRELEASED);
735                         self.teleport_time = time + 2;  // safety net
736                         return;
737                 }
738         }
739 };
740
741
742 void respawn(void)
743 {
744         CopyBody(1);
745         PutClientInServer();
746 }
747
748 void player_powerups (void)
749 {
750         if (cvar("g_minstagib"))
751         {
752                 self.effects = EF_FULLBRIGHT;
753                 if (self.items & IT_STRENGTH)
754                 {
755                         self.effects = EF_NODRAW;
756                         if (time > self.strength_finished)
757                         {
758                                 self.items = self.items - (self.items & IT_STRENGTH);
759                                 sprint(self, "^3Invisibility has worn off\n");
760                         }
761                 }
762                 else
763                 {
764                         if (time < self.strength_finished)
765                         {
766                                 self.items = self.items | IT_STRENGTH;
767                                 sprint(self, "^3You are invisible\n");
768                         }
769                 }
770                                 
771                 if (self.items & IT_INVINCIBLE)
772                 {
773                         if (time > self.invincible_finished)
774                         {
775                                 self.items = self.items - (self.items & IT_INVINCIBLE);
776                                 sprint(self, "^3Speed has worn off\n");
777                         }
778                 }
779                 else
780                 {
781                         if (time < self.invincible_finished)
782                         {
783                                 self.items = self.items | IT_INVINCIBLE;
784                                 sprint(self, "^3You are on speed\n");
785                         }
786                 }
787                 return;
788         }
789         
790         self.effects = self.effects - (self.effects & (EF_RED | EF_BLUE | EF_ADDITIVE | EF_FULLBRIGHT));
791         if (self.items & IT_STRENGTH)
792         {
793                 self.effects = self.effects | (EF_BLUE | EF_ADDITIVE | EF_FULLBRIGHT);
794                 if (time > self.strength_finished)
795                 {
796                         self.items = self.items - (self.items & IT_STRENGTH);
797                         sprint(self, "^3Strength has worn off\n");
798                 }
799         }
800         else
801         {
802                 if (time < self.strength_finished)
803                 {
804                         self.items = self.items | IT_STRENGTH;
805                         sprint(self, "^3Strength infuses your weapons with devestating power\n");
806                 }
807         }
808         if (self.items & IT_INVINCIBLE)
809         {
810                 self.effects = self.effects | (EF_RED | EF_ADDITIVE | EF_FULLBRIGHT);
811                 if (time > self.invincible_finished)
812                 {
813                         self.items = self.items - (self.items & IT_INVINCIBLE);
814                         sprint(self, "^3Shield has worn off\n");
815                 }
816         }
817         else
818         {
819                 if (time < self.invincible_finished)
820                 {
821                         self.items = self.items | IT_INVINCIBLE;
822                         sprint(self, "^3Shield surrounds you\n");
823                 }
824         }
825         
826         if (cvar("g_fullbrightplayers"))
827                 self.effects = EF_FULLBRIGHT;
828         
829 }
830
831 void player_regen (void)
832 {
833         float maxh, maxa, max_mod, regen_mod, rot_mod;
834         maxh = cvar("g_balance_health_stable");
835         maxa = cvar("g_balance_armor_stable");
836
837         if (cvar("g_minstagib"))
838         {
839                 maxh = 100;
840                 maxa = 0;
841                 return;
842         }
843
844         if(cvar("g_runematch"))
845         {
846                 max_mod = regen_mod = rot_mod = 1;
847                 if (self.runes & RUNE_REGEN)
848                 {
849                         if (self.runes & CURSE_VENOM) // do we have both rune/curse?
850                         {
851                                 regen_mod = cvar("g_balance_rune_regen_combo_regenrate");
852                                 max_mod = cvar("g_balance_rune_regen_combo_hpmod");
853                         }
854                         else
855                         {
856                                 regen_mod = cvar("g_balance_rune_regen_regenrate");
857                                 max_mod = cvar("g_balance_rune_regen_hpmod");
858                         }
859                 }
860                 else if (self.runes & CURSE_VENOM)
861                 {
862                         max_mod = cvar("g_balance_curse_venom_hpmod");
863                         if (self.runes & RUNE_REGEN) // do we have both rune/curse?
864                                 rot_mod = cvar("g_balance_rune_regen_combo_rotrate");
865                         else
866                                 rot_mod = cvar("g_balance_curse_venom_rotrate");
867                         //if (!self.runes & RUNE_REGEN)
868                         //      rot_mod = cvar("g_balance_curse_venom_rotrate");
869                 }
870                 maxh = maxh * max_mod;
871                 //maxa = maxa * max_mod;
872
873                 if (time > self.pauserotarmor_finished)
874                 {
875                         if (self.armorvalue > maxa)
876                                 self.armorvalue = bound(0, self.armorvalue + (maxa - self.armorvalue) * cvar("g_balance_armor_rot") * frametime, 1000);
877                 }
878                 if (time > self.pauserothealth_finished)
879                 {
880                         if (self.health > maxh)
881                                 self.health = bound(0, self.health + (maxh - self.health) * rot_mod*cvar("g_balance_health_rot") * frametime, 1000);
882                 }
883                 if (time > self.pauseregen_finished)
884                 {
885                         if (self.health < maxh)
886                                 self.health = bound(0, self.health + (maxh- self.health) * regen_mod*cvar("g_balance_health_regen") * frametime, 1000);
887                         if (self.armorvalue < maxa)
888                                 self.armorvalue = bound(0, self.armorvalue + (maxa - self.armorvalue) * cvar("g_balance_armor_regen") * frametime, 1000);
889                 }
890         }
891         else
892         {
893                 if (time > self.pauserothealth_finished)
894                 if (self.health > maxh)
895                         self.health = bound(0, self.health + (maxh - self.health) * cvar("g_balance_health_rot") * frametime, 1000);
896                 if (time > self.pauserotarmor_finished)
897                 if (self.armorvalue > maxa)
898                         self.armorvalue = bound(0, self.armorvalue + (maxa - self.armorvalue) * cvar("g_balance_armor_rot") * frametime, 1000);
899                 if (time > self.pauseregen_finished)
900                 {
901                         if (self.health < maxh)
902                                 self.health = bound(0, self.health + (maxh- self.health) * cvar("g_balance_health_regen") * frametime, 1000);
903                         if (self.armorvalue < maxa)
904                                 self.armorvalue = bound(0, self.armorvalue + (maxa - self.armorvalue) * cvar("g_balance_armor_regen") * frametime, 1000);
905                 }
906         }
907 }
908
909 /*
910 ======================
911 spectate mode routines
912 ======================
913 */
914 void SpectateCopy(entity spectatee) {
915         self.armortype = spectatee.armortype;
916         self.armorvalue = spectatee.armorvalue;
917         self.currentammo = spectatee.currentammo;
918         self.effects = spectatee.effects;
919         self.health = spectatee.health;
920         self.impulse = 0;
921         self.items = spectatee.items;
922         self.punchangle = spectatee.punchangle;
923         self.view_ofs = spectatee.view_ofs;
924         self.v_angle = spectatee.v_angle;
925         self.viewzoom = spectatee.viewzoom;
926         setorigin(self, spectatee.origin);
927         setsize(self, spectatee.mins, spectatee.maxs);
928 }
929
930 void SpectateUpdate() {
931         if (self != self.enemy) {
932                 SpectateCopy(self.enemy);
933                 msg_entity = self;
934                 WriteByte(MSG_ONE, SVC_SETANGLE);
935                 WriteAngle(MSG_ONE, self.enemy.v_angle_x);
936                 WriteAngle(MSG_ONE, self.enemy.v_angle_y);
937                 WriteAngle(MSG_ONE, self.enemy.v_angle_z);
938         }
939 }
940
941 float SpectateNext() {
942         other = find(self.enemy, classname, "player");
943         if (!other) {
944                 other = find(other, classname, "player");
945         }
946         if (other) {
947                 self.enemy = other;
948         }
949         if(self.enemy.classname == "player") {
950                 msg_entity = self;
951                 WriteByte(MSG_ONE, SVC_SETVIEW);
952                 WriteEntity(MSG_ONE, self.enemy);
953                 //stuffcmd(self, "set viewsize $tmpviewsize \n");
954                 SpectateUpdate();
955                 return 1;
956         } else {
957                 return 0;
958         }
959 }
960
961 /*
962 =============
963 PlayerPreThink
964
965 Called every frame for each client before the physics are run
966 =============
967 */
968 void PlayerPreThink (void)
969 {
970         if(self.classname == "player") {
971                 local vector m1, m2;
972
973 //              MauveBot_AI();
974
975 //              if(self.netname == "Wazat")
976 //                      bprint(strcat(self.classname, "\n"));
977
978                 CheckRules_Player();
979
980                 if (intermission_running)
981                 {
982                         IntermissionThink ();   // otherwise a button could be missed between
983                         return;                                 // the think tics
984                 }
985
986                 if (self.deadflag != DEAD_NO)
987                 {
988                         player_anim();
989                         weapon_freeze();
990                         if (self.deadflag == DEAD_DYING)
991                         {
992                                 if (time > self.dead_time)
993                                         self.deadflag = DEAD_DEAD;
994                         }
995                         else if (self.deadflag == DEAD_DEAD)
996                         {
997                                 if (!self.button0 && !self.button2 && !self.button3)
998                                         self.deadflag = DEAD_RESPAWNABLE;
999                         }
1000                         else if (self.deadflag == DEAD_RESPAWNABLE)
1001                         {
1002                                 if (self.button0  || 
1003                                     self.button2  || 
1004                                     self.button3  || 
1005                                     self.button4  || 
1006                                     cvar("g_lms") || 
1007                                     cvar("g_forced_respawn"))
1008                                         respawn();
1009                         }
1010                         return;
1011                 }
1012
1013                 if (self.button5)
1014                 {
1015                         if (!self.crouch)
1016                         {
1017                                 self.crouch = TRUE;
1018                                 self.view_ofs = PL_CROUCH_VIEW_OFS;
1019                                 setsize (self, PL_CROUCH_MIN, PL_CROUCH_MAX);
1020                         }
1021                 }
1022                 else
1023                 {
1024                         if (self.crouch)
1025                         {
1026                                 tracebox(self.origin, PL_MIN, PL_MAX, self.origin, FALSE, self);
1027                                 if (!trace_startsolid)
1028                                 {
1029                                         self.crouch = FALSE;
1030                                         self.view_ofs = PL_VIEW_OFS;
1031                                         setsize (self, PL_MIN, PL_MAX);
1032                                 }
1033                         }
1034                 }
1035
1036                 if(cvar("sv_defaultcharacter") == 1) {
1037                         local string defaultmodel;
1038                         defaultmodel = CheckPlayerModel(cvar_string("sv_defaultplayermodel"));
1039                         
1040                         if (defaultmodel != self.model)
1041                         {
1042                                 m1 = self.mins;
1043                                 m2 = self.maxs;
1044                                 precache_model (defaultmodel);
1045                                 setmodel (self, defaultmodel);
1046                                 setsize (self, m1, m2);
1047                         }
1048
1049                         if (self.skin != stof(cvar_string("sv_defaultplayerskin")))
1050                                 self.skin = stof(cvar_string("sv_defaultplayerskin"));
1051                 } else {
1052                         if (self.playermodel != self.model)
1053                         {
1054                                 self.playermodel = CheckPlayerModel(self.playermodel);
1055                                 m1 = self.mins;
1056                                 m2 = self.maxs;
1057                                 precache_model (self.playermodel);
1058                                 setmodel (self, self.playermodel);
1059                                 setsize (self, m1, m2);
1060                         }
1061
1062                         if (self.skin != stof(self.playerskin))
1063                                 self.skin = stof(self.playerskin);
1064                 }
1065                 // Savage: Check for nameless players
1066                 if (strlen(self.netname) < 1) {
1067                         self.netname = "Player";
1068                         stuffcmd(self, "name Player\n");
1069                 }
1070         
1071                 GrapplingHookFrame();
1072
1073                 W_WeaponFrame();
1074
1075                 if (self.button4 || (self.weapon == WEP_NEX && self.button3))
1076                 {
1077                         if (cvar("g_minstagib") && self.button3)
1078                         {
1079                                 if (self.jump_interval <= (time + 0.1))
1080                                 {
1081                                         self.jump_interval = time + 1;
1082                                         weapon_doattack(laser_check, laser_check, W_Laser_Attack);
1083                                 }
1084                         }
1085                         else if (self.viewzoom > 0.4)
1086                                 self.viewzoom = max (0.4, self.viewzoom - frametime * 2);
1087                 }
1088                 else if (self.viewzoom < 1.0)
1089                         self.viewzoom = min (1.0, self.viewzoom + frametime);
1090
1091
1092                 if (self.button2)
1093                         PlayerJump ();
1094                 else
1095                         self.flags = self.flags | FL_JUMPRELEASED;
1096
1097                 player_powerups();
1098                 player_regen();
1099                 player_anim();
1100
1101                 //self.angles_y=self.v_angle_y + 90;   // temp
1102
1103                 if (self.waterlevel == 2)
1104                         CheckWaterJump ();
1105
1106                 //if (TetrisPreFrame()) return;
1107         } else if(self.classname == "observer") {
1108                 
1109                 if (self.flags & FL_JUMPRELEASED) {
1110                         if (self.button2) {
1111                                 if(!cvar("teamplay")) {
1112                                         self.flags = self.flags & !FL_JUMPRELEASED;
1113                                         self.classname = "player";
1114                                         if(!cvar("g_lms"))
1115                                                 bprint (strcat("^4", self.netname, "^4 is playing now\n"));
1116                                         PutClientInServer();
1117                                         centerprint(self,"");
1118                                         return;
1119                                 } else {
1120                                         self.flags = self.flags & !FL_JUMPRELEASED;
1121                                         stuffcmd(self,"menu_showteamselect\n");
1122                                         return;
1123                                 }
1124                         } else if(self.button0) {
1125                                 self.flags = self.flags & !FL_JUMPRELEASED;
1126                                 if(SpectateNext() == 1) {
1127                                         self.classname = "spectator";
1128                                 } 
1129                         }
1130                 } else {
1131                         if (!(self.button0 || self.button2)) {
1132                                 self.flags = self.flags | FL_JUMPRELEASED;
1133                         }
1134                 }
1135                 if(cvar("g_lms") && self.frags == 0)
1136                         centerprint(self, "\n\n\n^1You have no more lives left\nwait for next round\n\n\n^7press attack to spectate other players");
1137                 else if(cvar("g_lms") && self.frags == -1)
1138                         centerprint(self, "\n\n\n^1Match has already begun\nwait for next round\n\n\n^7press attack to spectate other players");
1139                 else
1140                         PrintWelcomeMessage(self);
1141                         //centerprint(self, "\n\n\npress jump to play\npress attack to spectate other players");
1142         } else if(self.classname == "spectator") {
1143                 
1144                 if (self.flags & FL_JUMPRELEASED) {
1145                         if(self.button0) {
1146                                 self.flags = self.flags & !FL_JUMPRELEASED;
1147                                 if(SpectateNext() == 1) {
1148                                         self.classname = "spectator";
1149                                 } else {
1150                                         self.classname = "observer";
1151                                         msg_entity = self;                                                                                                                                                     
1152                                         WriteByte(MSG_ONE, SVC_SETVIEW);                                                                                                                                       
1153                                         WriteEntity(MSG_ONE, self);   
1154                                         PutClientInServer();                                    
1155                                 }
1156                         } else if (self.button3) {
1157                                 self.flags = self.flags & !FL_JUMPRELEASED;
1158                                 self.classname = "observer";
1159                                 msg_entity = self;                                                                                                                                                     
1160                                 WriteByte(MSG_ONE, SVC_SETVIEW);                                                                                                                                       
1161                                 WriteEntity(MSG_ONE, self);   
1162                                 PutClientInServer();
1163                         } else {
1164                                 SpectateUpdate();
1165                         }
1166         } else {
1167                 if (!(self.button0 || self.button3)) {
1168                         self.flags = self.flags | FL_JUMPRELEASED;
1169                 }
1170                 }
1171                 centerprint(self, strcat("spectating ", self.enemy.netname, "\n\n\n^7press attack for next player\npress attack2 for free fly mode"));
1172                 
1173         }
1174         VoteThink();
1175 }
1176
1177 /*
1178 =============
1179 PlayerPostThink
1180
1181 Called every frame for each client after the physics are run
1182 =============
1183 */
1184 void PlayerPostThink (void)
1185 {
1186         if(self.classname == "player") {
1187                 CheckRules_Player();
1188                 UpdateChatBubble();
1189                 UpdatePlayerColors();
1190                 if (self.deadflag == DEAD_NO)
1191                 if (self.impulse)
1192                         ImpulseCommands ();
1193                 if (intermission_running)
1194                         return;         // intermission or finale
1195
1196                 //PrintWelcomeMessage(self);
1197                 //if (TetrisPostFrame()) return;
1198         } else if (self.classname == "observer") {
1199                 //do nothing
1200         } else if (self.classname == "spectator") {
1201                 //do nothing
1202         }
1203 }