]> icculus.org git repositories - divverent/nexuiz.git/blob - qcsrc/gamec/cl_client.c
additional stuffcmd for player name change
[divverent/nexuiz.git] / 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 /*
11 =============
12 SelectSpawnPoint
13
14 Finds a point to respawn
15 =============
16 */
17 entity SelectSpawnPoint (void)
18 {
19         local entity spot, thing, best, player, playerlist;
20         local float pcount, rating, bestrating;
21
22         spot = find (world, classname, "testplayerstart");
23         if (spot)
24                 return spot;
25
26         best = world;
27         bestrating = -1;
28         playerlist = findchain(classname, "player");
29         spot = find(world, classname, "info_player_deathmatch");
30         while (spot)
31         {
32                 rating = random() * 256;
33                 player = playerlist;
34                 while (player)
35                 {
36                         if (player != self)
37                                 rating = rating + vlen(player.origin - spot.origin);
38                         player = player.chain;
39                 }
40                 if (bestrating < rating)
41                 {
42                         best = spot;
43                         bestrating = rating;
44                 }
45                 spot = find(spot, classname, "info_player_deathmatch");
46         }
47         spot = best;
48
49         if (!spot)
50                 error ("PutClientInServer: no info_player_start on level");
51
52         return spot;
53 }
54
55 /*
56 =============
57 CheckPlayerModel
58
59 Checks if the argument string can be a valid playermodel.
60 Returns a valid one in doubt.
61 =============
62 */
63 string CheckPlayerModel(string playermodel) {
64         if( substring(playermodel,0,14) != "models/player/") playermodel = "models/player/marine.zym";
65
66         /* Possible Fixme: Check if server can open the model?
67            This would kill custom models, however. */
68
69         return playermodel;
70 }
71
72
73 /*
74 =============
75 PutClientInServer
76
77 Called when a client spawns in the server
78 =============
79 */
80 void PutClientInServer (void)
81 {
82         entity  spot;
83
84         spot = SelectSpawnPoint ();
85
86         self.classname = "player";
87         self.movetype = MOVETYPE_WALK;
88         self.solid = SOLID_SLIDEBOX;
89         self.flags = FL_CLIENT;
90         self.takedamage = DAMAGE_AIM;
91         self.effects = 0;
92         self.health = cvar("g_balance_health_start");
93         self.armorvalue = cvar("g_balance_armor_start");
94         self.damageforcescale = 2;
95         self.death_time = 0;
96         self.dead_time = 0;
97         self.dead_frame = 0;
98         self.die_frame = 0;
99         self.alpha = 0;
100         self.scale = 0;
101         self.fade_time = 0;
102         self.pain_frame = 0;
103         self.pain_finished = 0;
104         self.strength_finished = 0;
105         self.invincible_finished = 0;
106         //self.speed_finished = 0;
107         //self.slowmo_finished = 0;
108         // players have no think function
109         self.think = SUB_Null;
110         self.nextthink = 0;
111
112         self.deadflag = DEAD_NO;
113
114         self.angles = spot.angles;
115         self.fixangle = TRUE; // turn this way immediately
116         self.velocity = '0 0 0';
117         self.avelocity = '0 0 0';
118         self.punchangle = '0 0 0';
119         self.punchvector = '0 0 0';
120
121         self.viewzoom = 0.6;
122
123
124         self.playermodel = CheckPlayerModel(self.playermodel);
125
126         precache_model (self.playermodel);
127         setmodel (self, self.playermodel);
128         self.skin = stof(self.playerskin);
129         self.crouch = FALSE;
130         self.view_ofs = PL_VIEW_OFS;
131         setsize (self, PL_MIN, PL_MAX);
132         setorigin (self, spot.origin + '0 0 1' * (1 - self.mins_z - 24));
133         // don't reset back to last position, even if new position is stuck in solid
134         self.oldorigin = self.origin;
135
136 //      self.items = IT_LASER | IT_UZI| IT_SHOTGUN | IT_GRENADE_LAUNCHER | IT_ELECTRO | IT_CRYLINK | IT_NEX | IT_HAGAR | IT_ROCKET_LAUNCHER;
137 //      self.weapon = IT_UZI;
138
139         if (cvar("g_instagib") == 1)
140         {
141                 self.items = IT_NEX;
142                 self.switchweapon = WEP_NEX;
143                 self.ammo_shells = 0;
144                 self.ammo_nails = 0;
145                 self.ammo_rockets = 0;
146                 self.ammo_cells = 999;
147         }
148         else if (cvar("g_rocketarena") == 1)
149         {
150                 self.items = IT_ROCKET_LAUNCHER;
151                 self.switchweapon = WEP_ROCKET_LAUNCHER;
152                 self.ammo_shells = 0;
153                 self.ammo_nails = 0;
154                 self.ammo_rockets = 999;
155                 self.ammo_cells = 0;
156         }
157         else
158         {
159                 self.items = IT_LASER | IT_SHOTGUN;
160                 self.switchweapon = WEP_SHOTGUN;
161                 self.ammo_shells = 50;
162                 self.ammo_nails = 0;
163                 self.ammo_rockets = 0;
164                 self.ammo_cells = 0;
165         }
166
167         if (cvar("g_fullbrightplayers") == 1)
168                 self.effects = EF_FULLBRIGHT;
169
170         self.event_damage = PlayerDamage;
171
172         self.statdraintime = time + 5;
173         self.button0 = self.button1 = self.button2 = self.button3 = 0;
174
175         /*
176         W_UpdateWeapon();
177         W_UpdateAmmo();
178         */
179         CL_SpawnWeaponentity();
180
181         //stuffcmd(self, "chase_active 0");
182 }
183
184 /*
185 =============
186 SetNewParms
187 =============
188 */
189 void SetNewParms (void)
190 {
191
192 }
193
194 /*
195 =============
196 SetChangeParms
197 =============
198 */
199 void SetChangeParms (void)
200 {
201
202 }
203
204 /*
205 =============
206 ClientKill
207
208 Called when a client types 'kill' in the console
209 =============
210 */
211 void ClientKill (void)
212 {
213         Damage(self, self, self, 100000, DEATH_KILL, self.origin, '0 0 0');
214 }
215
216 /*
217 =============
218 ClientConnect
219
220 Called when a client connects to the server
221 =============
222 */
223 void ClientConnect (void)
224 {
225         ClientInRankings();
226         bprint ("^4",self.netname);
227         bprint (" connected\n");
228         stuffcmd(self, strcat("exec maps/", mapname, ".cfg\n"));
229         // send prediction settings to the client
230         stuffcmd(self, strcat("cl_movement_maxspeed ", ftos(cvar("sv_maxspeed")), "\n"));
231         stuffcmd(self, strcat("cl_movement_maxairspeed ", ftos(cvar("sv_maxairspeed")), "\n"));
232         stuffcmd(self, strcat("cl_movement_accelerate ", ftos(cvar("sv_accelerate")), "\n"));
233         stuffcmd(self, strcat("cl_movement_friction ", ftos(cvar("sv_friction")), "\n"));
234         stuffcmd(self, strcat("cl_movement_stopspeed ", ftos(cvar("sv_stopspeed")), "\n"));
235         stuffcmd(self, strcat("cl_movement_jumpvelocity ", ftos(cvar("g_balance_jumpheight")), "\n"));
236         stuffcmd(self, strcat("cl_movement_stepheight ", ftos(cvar("sv_stepheight")), "\n"));
237         stuffcmd(self, strcat("cl_movement_edgefriction 0\n"));
238 }
239
240 /*
241 =============
242 ClientDisconnect
243
244 Called when a client disconnects from the server
245 =============
246 */
247 void ClientDisconnect (void)
248 {
249         ClientDisconnected();
250         bprint ("^4",self.netname);
251         bprint (" disconnected\n");
252 }
253
254 .entity chatbubbleentity;
255 .float buttonchat;
256 void() ChatBubbleThink =
257 {
258         self.nextthink = time;
259         if (!self.owner.modelindex || self.owner.chatbubbleentity != self)
260         {
261                 remove(self);
262                 return;
263         }
264         setorigin(self, self.owner.origin + '0 0 15' + self.owner.maxs_z * '0 0 1');
265         if (self.owner.buttonchat)
266                 self.effects = 0;
267         else
268                 self.effects = EF_NODRAW;
269 };
270
271 void() UpdateChatBubble =
272 {
273         if (!self.modelindex)
274                 return;
275         // spawn a chatbubble entity if needed
276         if (!self.chatbubbleentity)
277         {
278                 self.chatbubbleentity = spawn();
279                 self.chatbubbleentity.owner = self;
280                 self.chatbubbleentity.think = ChatBubbleThink;
281                 self.chatbubbleentity.nextthink = time;
282                 setmodel(self.chatbubbleentity, "models/misc/chatbubble.spr");
283                 setorigin(self.chatbubbleentity, self.origin + '0 0 15' + self.maxs_z * '0 0 1');
284                 self.chatbubbleentity.effects = EF_NODRAW;
285         }
286 }
287
288 // LordHavoc: this hack will be removed when proper _pants/_shirt layers are
289 // added to the model skins
290 void() UpdateColorModHack =
291 {
292         local float c;
293         c = self.clientcolors & 15;
294         // LordHavoc: only bothering to support white, green, red, yellow, blue
295              if (teamplay == 0) self.colormod = '0 0 0';
296         else if (c ==  0) self.colormod = '1.00 1.00 1.00';
297         else if (c ==  3) self.colormod = '0.10 1.73 0.10';
298         else if (c ==  4) self.colormod = '1.73 0.10 0.10';
299         else if (c == 12) self.colormod = '1.22 1.22 0.10';
300         else if (c == 13) self.colormod = '0.10 0.10 1.73';
301         else self.colormod = '1 1 1';
302 };
303
304 /*
305 =============
306 PlayerJump
307
308 When you press the jump key
309 =============
310 */
311 void PlayerJump (void)
312 {
313         if (self.waterlevel >= 2)
314         {
315                 if (self.watertype == CONTENT_WATER)
316                         self.velocity_z = 200;
317                 else if (self.watertype == CONTENT_SLIME)
318                         self.velocity_z = 80;
319                 else
320                         self.velocity_z = 50;
321
322                 return;
323         }
324
325
326         if (!(self.flags & FL_ONGROUND))
327                 return;
328
329         if (!(self.flags & FL_JUMPRELEASED))
330                 return;
331
332         self.velocity_z = self.velocity_z + cvar("g_balance_jumpheight");
333
334         self.flags = self.flags - FL_ONGROUND;
335         self.flags = self.flags - FL_JUMPRELEASED;
336 }
337
338 .float watersound_finished;
339 void() WaterMove =
340 {
341         if (self.movetype == MOVETYPE_NOCLIP)
342                 return;
343         if (self.health < 0)
344                 return;
345
346         if (self.waterlevel != 3)
347         {
348                 self.air_finished = time + 12;
349                 self.dmg = 2;
350         }
351         else if (self.air_finished < time)
352         {       // drown!
353                 if (self.pain_finished < time)
354                 {
355                         Damage (self, world, world, 5, DEATH_DROWN, self.origin, '0 0 0');
356                         self.pain_finished = time + 0.5;
357                 }
358         }
359
360         if (!self.waterlevel)
361         {
362                 if (self.flags & FL_INWATER)
363                 {
364                         // play leave water sound
365                         self.flags = self.flags - FL_INWATER;
366                 }
367                 return;
368         }
369
370         if (self.watersound_finished < time)
371         {
372                 self.watersound_finished = time + 0.5;
373                 if (self.watertype == CONTENT_LAVA)
374                         sound (self, CHAN_BODY, "player/lava.wav", 1, ATTN_NORM);
375                 if (self.watertype == CONTENT_SLIME)
376                         sound (self, CHAN_BODY, "player/slime.wav", 1, ATTN_NORM);
377                 //if (self.watertype == CONTENT_WATER)
378                 //      sound (self, CHAN_BODY, "player/water.wav", 1, ATTN_NORM);
379         }
380
381         if (self.watertype == CONTENT_LAVA)
382         {       // do damage
383                 if (self.dmgtime < time)
384                 {
385                         self.dmgtime = time + 0.1;
386                         Damage (self, world, world, 3 * self.waterlevel, DEATH_LAVA, self.origin, '0 0 0');
387                 }
388         }
389         else if (self.watertype == CONTENT_SLIME)
390         {       // do damage
391                 if (self.dmgtime < time)
392                 {
393                         self.dmgtime = time + 0.1;
394                         Damage (self, world, world, 1 * self.waterlevel, DEATH_SLIME, self.origin, '0 0 0');
395                 }
396         }
397
398         if ( !(self.flags & FL_INWATER) )
399         {
400
401                 //if (self.watertype == CONTENT_LAVA)
402                 //      sound (self, CHAN_BODY, "player/inlava.wav", 1, ATTN_NORM);
403                 //if (self.watertype == CONTENT_WATER)
404                 //      sound (self, CHAN_BODY, "player/inh2o.wav", 1, ATTN_NORM);
405                 //if (self.watertype == CONTENT_SLIME)
406                 //      sound (self, CHAN_BODY, "player/slimbrn2.wav", 1, ATTN_NORM);
407
408                 self.flags = self.flags + FL_INWATER;
409                 self.dmgtime = 0;
410         }
411 };
412
413 void() CheckWaterJump =
414 {
415         local vector start, end;
416
417 // check for a jump-out-of-water
418         makevectors (self.angles);
419         start = self.origin;
420         start_z = start_z + 8;
421         v_forward_z = 0;
422         normalize(v_forward);
423         end = start + v_forward*24;
424         traceline (start, end, TRUE, self);
425         if (trace_fraction < 1)
426         {       // solid at waist
427                 start_z = start_z + self.maxs_z - 8;
428                 end = start + v_forward*24;
429                 self.movedir = trace_plane_normal * -50;
430                 traceline (start, end, TRUE, self);
431                 if (trace_fraction == 1)
432                 {       // open at eye level
433                         self.flags = self.flags | FL_WATERJUMP;
434                         self.velocity_z = 225;
435                         self.flags = self.flags - (self.flags & FL_JUMPRELEASED);
436                         self.teleport_time = time + 2;  // safety net
437                         return;
438                 }
439         }
440 };
441
442
443 void respawn(void)
444 {
445         CopyBody(1);
446         PutClientInServer();
447 }
448
449 void player_powerups (void)
450 {
451         self.effects = self.effects - (self.effects & (EF_RED | EF_BLUE | EF_ADDITIVE | EF_FULLBRIGHT));
452         if (self.items & IT_STRENGTH)
453         {
454                 self.effects = self.effects | (EF_BLUE | EF_ADDITIVE | EF_FULLBRIGHT);
455                 if (time > self.strength_finished)
456                 {
457                         self.items = self.items - (self.items & IT_STRENGTH);
458                         sprint(self, "^3Strength has worn off\n");
459                 }
460         }
461         else
462         {
463                 if (time < self.strength_finished)
464                 {
465                         self.items = self.items | IT_STRENGTH;
466                         sprint(self, "^3Strength infuses your weapons with devestating power\n");
467                 }
468         }
469         if (self.items & IT_INVINCIBLE)
470         {
471                 self.effects = self.effects | (EF_RED | EF_ADDITIVE | EF_FULLBRIGHT);
472                 if (time > self.invincible_finished)
473                 {
474                         self.items = self.items - (self.items & IT_INVINCIBLE);
475                         sprint(self, "^3Shield has worn off\n");
476                 }
477         }
478         else
479         {
480                 if (time < self.invincible_finished)
481                 {
482                         self.items = self.items | IT_INVINCIBLE;
483                         sprint(self, "^3Shield surrounds you\n");
484                 }
485         }
486 }
487
488 void player_regen (void)
489 {
490         local float maxh;
491         local float maxa;
492         maxh = cvar("g_balance_health_stable");
493         maxa = cvar("g_balance_armor_stable");
494         if (self.health > maxh)
495                 self.health = bound(0, self.health + (maxh - self.health) * cvar("g_balance_health_rot") * frametime, 1000);
496         else if (time > self.pain_finished)
497                 self.health = bound(0, self.health + (maxh- self.health) * cvar("g_balance_health_regen") * frametime, 1000);
498         if (self.armorvalue > maxa)
499                 self.armorvalue = bound(0, self.armorvalue + (maxa - self.armorvalue) * cvar("g_balance_armor_rot") * frametime, 1000);
500         else if (time > self.pain_finished)
501                 self.armorvalue = bound(0, self.armorvalue + (maxa - self.armorvalue) * cvar("g_balance_armor_regen") * frametime, 1000);
502 }
503
504 /*
505 =============
506 PlayerPreThink
507
508 Called every frame for each client before the physics are run
509 =============
510 */
511 .float attack_finished;
512 void PlayerPreThink (void)
513 {
514         local vector m1, m2;
515
516         if (BotPreFrame())
517                 return;
518
519         CheckRules_Player();
520
521         if (intermission_running)
522         {
523                 IntermissionThink ();   // otherwise a button could be missed between
524                 return;                                 // the think tics
525         }
526
527         if (self.deadflag != DEAD_NO)
528         {
529                 player_anim();
530                 weapon_freeze();
531                 if (self.deadflag == DEAD_DYING)
532                 {
533                         if (time > self.dead_time)
534                                 self.deadflag = DEAD_DEAD;
535                 }
536                 else if (self.deadflag == DEAD_DEAD)
537                 {
538                         if (!self.button0 && !self.button2 && !self.button3)
539                                 self.deadflag = DEAD_RESPAWNABLE;
540                 }
541                 else if (self.deadflag == DEAD_RESPAWNABLE)
542                 {
543                         if (self.button0 || self.button2 || self.button3  || self.button4)
544                                 respawn();
545                 }
546                 return;
547         }
548
549         if (self.button5)
550         {
551                 if (!self.crouch)
552                 {
553                         self.crouch = TRUE;
554                         self.view_ofs = PL_CROUCH_VIEW_OFS;
555                         setsize (self, PL_CROUCH_MIN, PL_CROUCH_MAX);
556                 }
557         }
558         else
559         {
560                 if (self.crouch)
561                 {
562                         tracebox(self.origin, PL_MIN, PL_MAX, self.origin, FALSE, self);
563                         if (!trace_startsolid)
564                         {
565                                 self.crouch = FALSE;
566                                 self.view_ofs = PL_VIEW_OFS;
567                                 setsize (self, PL_MIN, PL_MAX);
568                         }
569                 }
570         }
571
572         if (self.playermodel != self.model)
573         {
574                 self.playermodel = CheckPlayerModel(self.playermodel);
575                 m1 = self.mins;
576                 m2 = self.maxs;
577                 precache_model (self.playermodel);
578                 setmodel (self, self.playermodel);
579                 setsize (self, m1, m2);
580         }
581         
582         // Savage: Check for nameless players
583         if (strlen(self.netname) < 1) {
584                 self.netname = "Player";
585                 stuffcmd(self, "name Player\n");
586         }
587
588         if (self.skin != stof(self.playerskin))
589                 self.skin = stof(self.playerskin);
590
591         W_WeaponFrame();
592
593         if (self.button4 || (self.weapon == WEP_NEX && self.button3))
594         {
595                 if (self.viewzoom > 0.4)
596                         self.viewzoom = max (0.4, self.viewzoom - frametime * 2);
597         }
598         else if (self.viewzoom < 1.0)
599                 self.viewzoom = min (1.0, self.viewzoom + frametime);
600
601
602         if (self.button2)
603                 PlayerJump ();
604         else
605                 self.flags = self.flags | FL_JUMPRELEASED;
606
607
608         player_powerups();
609         player_regen();
610         player_anim();
611
612         //self.angles_y=self.v_angle_y + 90;   // temp
613
614         WaterMove ();
615         if (self.waterlevel == 2)
616                 CheckWaterJump ();
617
618         //if (TetrisPreFrame()) return;
619 }
620
621 /*
622 =============
623 PlayerPostThink
624
625 Called every frame for each client after the physics are run
626 =============
627 */
628 void PlayerPostThink (void)
629 {
630         float soundrandom;
631         if (BotPostFrame())
632                 return;
633         CheckRules_Player();
634         UpdateChatBubble();
635         UpdateColorModHack();
636         if (self.deadflag == DEAD_NO)
637         if (self.impulse)
638                 ImpulseCommands ();
639         if (intermission_running)
640                 return;         // intermission or finale
641
642         // VorteX: landing on floor, landing damage etc.
643         // LordHavoc: removed 'big fall' death code that VorteX added
644         if (self.flags & FL_ONGROUND)
645         {
646                 if (self.jump_flag < -100 && !self.watertype == CONTENT_WATER) // HitGround
647                 {
648                         soundrandom = random() * 4;
649                         if (soundrandom < 1)
650                                 sound (self, CHAN_BODY, "misc/hitground1.wav", 1, ATTN_NORM);
651                         else if (soundrandom < 2)
652                                 sound (self, CHAN_BODY, "misc/hitground2.wav", 1, ATTN_NORM);
653                         else if (soundrandom < 3)
654                                 sound (self, CHAN_BODY, "misc/hitground3.wav", 1, ATTN_NORM);
655                         else if (soundrandom < 4)
656                                 sound (self, CHAN_BODY, "misc/hitground4.wav", 1, ATTN_NORM);
657                         if (self.jump_flag < -650) // landing damage
658                         {
659                                 local float dm;
660                                 dm = bound(0, 0.1*(fabs(self.jump_flag) - 600), 5);
661                                 Damage (self, world, world, dm, DEATH_FALL, self.origin, '0 0 0');
662                         }
663                         self.jump_flag = 0;
664                 }
665         }
666         else
667                 self.jump_flag = self.velocity_z;
668
669         //if (TetrisPostFrame()) return;
670 }