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