]> icculus.org git repositories - divverent/nexuiz.git/blob - qcsrc/gamec/cl_client.c
implemented timelimit, fraglimit, end of level intermissions, and data/maplist.cfg...
[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;
20         local float pcount;
21
22         spot = find (world, classname, "testplayerstart");
23         if (spot)
24                 return spot;
25
26         spot = lastspawn;
27         while (1)
28         {
29                 spot = find(spot, classname, "info_player_deathmatch");
30                 if (spot != world)
31                 {
32                         if (spot == lastspawn)
33                                 return lastspawn;
34                         pcount = 0;
35                         thing = findradius(spot.origin, 70);
36                         while(thing)
37                         {
38                                 if (thing.classname == "player")
39                                         pcount = pcount + 1;
40                                 thing = thing.chain;
41                         }
42                         if (pcount == 0)
43                         {
44                                 lastspawn = spot;
45                                 return spot;
46                         }
47                 }
48         }
49
50         spot = find (world, classname, "info_player_start");
51         if (!spot)
52                 error ("PutClientInServer: no info_player_start on level");
53
54         return spot;
55 }
56
57
58 /*
59 =============
60 PutClientInServer
61
62 Called when a client spawns in the server
63 =============
64 */
65 void PutClientInServer (void)
66 {
67         entity  spot;
68         float mdlrandom;
69
70         spot = SelectSpawnPoint ();
71
72         self.classname = "player";
73         self.movetype = MOVETYPE_WALK;
74         self.solid = SOLID_SLIDEBOX;
75         self.flags = FL_CLIENT;
76         self.takedamage = DAMAGE_YES;
77         self.effects = 0;
78         self.health = cvar("g_balance_health");
79         self.damageforcescale = 2;
80         self.death_time = 0;
81         self.dead_time = 0;
82         self.dead_frame = 0;
83         self.die_frame = 0;
84         self.alpha = 0;
85         self.scale = 0;
86         self.fade_time = 0;
87         self.pain_frame = 0;
88         self.pain_finished = 0;
89         self.strength_finished = 0;
90         self.invincible_finished = 0;
91         self.speed_finished = 0;
92         self.slowmo_finished = 0;
93         // players have no think function
94         self.think = nullfunction;
95         self.nextthink = 0;
96
97         self.deadflag = DEAD_NO;
98
99         self.view_ofs = PL_VIEW_OFS;
100         self.angles = spot.angles;
101         self.fixangle = TRUE; // turn this way immediately
102         self.velocity = '0 0 0';
103         self.avelocity = '0 0 0';
104         self.punchangle = '0 0 0';
105         self.punchvector = '0 0 0';
106
107         self.viewzoom = 0.6;
108
109         setmodel (self, self.playermodel);
110         self.skin = stof(self.playerskin);
111         setsize (self, PL_MIN, PL_MAX);
112         setorigin (self, spot.origin + '0 0 1' * (1 - self.mins_z - 24));
113         // don't reset back to last position, even if new position is stuck in solid
114         self.oldorigin = self.origin;
115
116 //      self.items = IT_LASER | IT_UZI| IT_SHOTGUN | IT_GRENADE_LAUNCHER | IT_ELECTRO | IT_CRYLINK | IT_NEX | IT_HAGAR | IT_ROCKET_LAUNCHER;
117 //      self.weapon = IT_UZI;
118
119         if (cvar("g_instagib") == 1)
120         {
121                 self.items = IT_NEX;
122                 self.switchweapon = WEP_NEX;
123                 self.ammo_shells = 0;
124                 self.ammo_nails = 0;
125                 self.ammo_rockets = 0;
126                 self.ammo_cells = 999;
127         }
128         else if (cvar("g_rocketarena") == 1)
129         {
130                 self.items = IT_ROCKET_LAUNCHER;
131                 self.switchweapon = WEP_ROCKET_LAUNCHER;
132                 self.ammo_shells = 0;
133                 self.ammo_nails = 0;
134                 self.ammo_rockets = 999;
135                 self.ammo_cells = 0;
136         }
137         else
138         {
139                 self.items = IT_LASER | IT_SHOTGUN;
140                 self.switchweapon = WEP_SHOTGUN;
141                 self.ammo_shells = 35;
142                 self.ammo_nails = 0;
143                 self.ammo_rockets = 0;
144                 self.ammo_cells = 0;
145         }
146
147         if (cvar("g_fullbrightplayers") == 1)
148                 self.effects = EF_FULLBRIGHT;
149
150         self.event_damage = PlayerDamage;
151
152         self.statdraintime = time + 5;
153         self.button0 = self.button1 = self.button2 = self.button3 = 0;
154
155         /*
156         W_UpdateWeapon();
157         W_UpdateAmmo();
158         */
159         CL_SpawnWeaponentity();
160
161         //stuffcmd(self, "chase_active 0");
162 }
163
164 /*
165 =============
166 SetNewParms
167 =============
168 */
169 void SetNewParms (void)
170 {
171
172 }
173
174 /*
175 =============
176 SetChangeParms
177 =============
178 */
179 void SetChangeParms (void)
180 {
181
182 }
183
184 /*
185 =============
186 ClientKill
187
188 Called when a client types 'kill' in the console
189 =============
190 */
191 void ClientKill (void)
192 {
193         Damage(self, self, self, 100000, DEATH_KILL, self.origin, '0 0 0');
194 }
195
196 /*
197 =============
198 ClientConnect
199
200 Called when a client connects to the server
201 =============
202 */
203 void ClientConnect (void)
204 {
205         ClientInRankings();
206         bprint ("^3",self.netname);
207         bprint (" connected\n");
208         stuffcmd(self, "exec maps/");
209         stuffcmd(self, mapname);
210         stuffcmd(self, ".cfg\n");
211 }
212
213 /*
214 =============
215 ClientDisconnect
216
217 Called when a client disconnects from the server
218 =============
219 */
220 void ClientDisconnect (void)
221 {
222         ClientDisconnected();
223         bprint ("^3",self.netname);
224         bprint (" disconnected\n");
225 }
226
227 /*
228 =============
229 PlayerJump
230
231 When you press the jump key
232 =============
233 */
234 void PlayerJump (void)
235 {
236         if (self.waterlevel >= 2)
237         {
238                 if (self.watertype == CONTENT_WATER)
239                         self.velocity_z = 200;
240                 else if (self.watertype == CONTENT_SLIME)
241                         self.velocity_z = 80;
242                 else
243                         self.velocity_z = 50;
244
245                 return;
246         }
247
248
249         if (!(self.flags & FL_ONGROUND))
250                 return;
251
252         if (!(self.flags & FL_JUMPRELEASED))
253                 return;
254
255         if (self.items & IT_SPEED)
256                 self.velocity_z = self.velocity_z + POWERUP_SPEED_JUMPVELOCITY;
257         else
258                 self.velocity_z = self.velocity_z + cvar("g_balance_jumpheight");
259
260         self.flags = self.flags - FL_ONGROUND;
261         self.flags = self.flags - FL_JUMPRELEASED;
262 }
263
264 void() WaterMove =
265 {
266         if (self.movetype == MOVETYPE_NOCLIP)
267                 return;
268         if (self.health < 0)
269                 return;
270
271         if (self.waterlevel != 3)
272         {
273                 self.air_finished = time + 12;
274                 self.dmg = 2;
275         }
276         else if (self.air_finished < time)
277         {       // drown!
278                 if (self.pain_finished < time)
279                 {
280                         Damage (self, world, world, 5, DEATH_DROWN, '0 0 0', '0 0 0');
281                         self.pain_finished = time + 0.5;
282                 }
283         }
284
285         if (!self.waterlevel)
286         {
287                 if (self.flags & FL_INWATER)
288                 {
289                         // play leave water sound
290                         sound (self, CHAN_BODY, "misc/outwater.wav", 1, ATTN_NORM);
291                         self.flags = self.flags - FL_INWATER;
292                 }
293                 return;
294         }
295
296         if (self.watertype == CONTENT_LAVA)
297         {       // do damage
298                 if (self.dmgtime < time)
299                 {
300                         self.dmgtime = time + 0.1;
301                         Damage (self, world, world, 3 * self.waterlevel, DEATH_LAVA, '0 0 0', '0 0 0');
302                 }
303         }
304         else if (self.watertype == CONTENT_SLIME)
305         {       // do damage
306                 if (self.dmgtime < time)
307                 {
308                         self.dmgtime = time + 0.1;
309                         Damage (self, world, world, 1 * self.waterlevel, DEATH_SLIME, '0 0 0', '0 0 0');
310                 }
311         }
312
313         if ( !(self.flags & FL_INWATER) )
314         {
315
316                 //if (self.watertype == CONTENT_LAVA)
317                 //      sound (self, CHAN_BODY, "player/inlava.wav", 1, ATTN_NORM);
318                 //if (self.watertype == CONTENT_WATER)
319                 //      sound (self, CHAN_BODY, "player/inh2o.wav", 1, ATTN_NORM);
320                 //if (self.watertype == CONTENT_SLIME)
321                 //      sound (self, CHAN_BODY, "player/slimbrn2.wav", 1, ATTN_NORM);
322
323                 self.flags = self.flags + FL_INWATER;
324                 self.dmgtime = 0;
325         }
326 };
327
328 void() CheckWaterJump =
329 {
330         local vector start, end;
331
332 // check for a jump-out-of-water
333         makevectors (self.angles);
334         start = self.origin;
335         start_z = start_z + 8;
336         v_forward_z = 0;
337         normalize(v_forward);
338         end = start + v_forward*24;
339         traceline (start, end, TRUE, self);
340         if (trace_fraction < 1)
341         {       // solid at waist
342                 start_z = start_z + self.maxs_z - 8;
343                 end = start + v_forward*24;
344                 self.movedir = trace_plane_normal * -50;
345                 traceline (start, end, TRUE, self);
346                 if (trace_fraction == 1)
347                 {       // open at eye level
348                         self.flags = self.flags | FL_WATERJUMP;
349                         self.velocity_z = 225;
350                         self.flags = self.flags - (self.flags & FL_JUMPRELEASED);
351                         self.teleport_time = time + 2;  // safety net
352                         return;
353                 }
354         }
355 };
356
357
358 void respawn(void)
359 {
360         CopyBody(1);
361         PutClientInServer();
362 }
363
364 void player_powerups (void)
365 {
366         if (self.items & IT_STRENGTH)
367         {
368                 if (time > self.strength_finished)
369                 {
370                         self.items = self.items - (self.items & IT_STRENGTH);
371                         sprint(self, "Strength has worn off\n");
372                 }
373         }
374         else
375         {
376                 if (time < self.strength_finished)
377                 {
378                         self.items = self.items | IT_STRENGTH;
379                         sprint(self, "Strength infuses your weapons with devestating power\n");
380                 }
381         }
382         if (self.items & IT_INVINCIBLE)
383         {
384                 if (time > self.invincible_finished)
385                 {
386                         self.items = self.items - (self.items & IT_INVINCIBLE);
387                         sprint(self, "Invincible has worn off\n");
388                 }
389         }
390         else
391         {
392                 if (time < self.invincible_finished)
393                 {
394                         self.items = self.items | IT_INVINCIBLE;
395                         sprint(self, "Invincible shielding surrounds you\n");
396                 }
397         }
398         if (self.items & IT_SPEED)
399         {
400                 if (time > self.speed_finished)
401                 {
402                         self.items = self.items - (self.items & IT_SPEED);
403                         sprint(self, "Speed has worn off\n");
404                 }
405         }
406         else
407         {
408                 if (time < self.speed_finished)
409                 {
410                         self.items = self.items | IT_SPEED;
411                         sprint(self, "Speed makes you run faster than ever before\n");
412                 }
413         }
414         if (self.items & IT_SLOWMO)
415         {
416                 if (time > self.slowmo_finished)
417                 {
418                         self.items = self.items - (self.items & IT_SLOWMO);
419                         sprint(self, "Slow Motion has worn off\n");
420                 }
421         }
422         else
423         {
424                 if (time < self.slowmo_finished)
425                 {
426                         self.items = self.items | IT_SLOWMO;
427                         sprint(self, "Slow Motion slows time around you\n");
428                 }
429         }
430         /*
431         self.items = self.items - (self.items & (IT_STRENGTH + IT_INVINCIBLE + IT_SLOWMO + IT_SPEED));
432         if (time < self.strength_finished)
433                 self.items = self.items | IT_STRENGTH;
434         if (time < self.invincible_finished)
435                 self.items = self.items | IT_INVINCIBLE;
436         if (time < self.slowmo_finished)
437                 self.items = self.items | IT_SLOWMO;
438         if (time < self.speed_finished)
439                 self.items = self.items | IT_SPEED;
440         */
441 }
442
443 void player_regen (void)
444 {
445         self.health = bound(0, self.health + (100 - self.health) * cvar("g_balance_healthregen") * frametime, 1000);
446         if (self.armorvalue > 100)
447                 self.armorvalue = bound(100, self.armorvalue + (100 - self.armorvalue) * cvar("g_balance_armorrott") * frametime, 1000);
448 }
449
450 /*
451 =============
452 PlayerPreThink
453
454 Called every frame for each client before the physics are run
455 =============
456 */
457 .float attack_finished;
458 void PlayerPreThink (void)
459 {
460         local vector m1, m2;
461
462         if (BotPreFrame())
463                 return;
464
465         if (intermission_running)
466         {
467                 IntermissionThink ();   // otherwise a button could be missed between
468                 return;                                 // the think tics
469         }
470
471         CheckRules();
472
473         if (self.deadflag != DEAD_NO)
474         {
475                 player_anim();
476                 weapon_freeze();
477                 if (self.deadflag == DEAD_DYING)
478                 {
479                         if (time > self.dead_time)
480                                 self.deadflag = DEAD_DEAD;
481                 }
482                 else if (self.deadflag == DEAD_DEAD)
483                 {
484                         if (!self.button0 && !self.button2 && !self.button3)
485                                 self.deadflag = DEAD_RESPAWNABLE;
486                 }
487                 else if (self.deadflag == DEAD_RESPAWNABLE)
488                 {
489                         if (self.button0 || self.button2 || self.button3  || self.button4)
490                                 respawn();
491                 }
492                 return;
493         }
494
495         if (self.playermodel != self.model)
496         {
497                 m1 = self.mins;
498                 m2 = self.maxs;
499                 setmodel (self, self.playermodel);
500                 setsize (self, m1, m2);
501         }
502
503         if (self.skin != stof(self.playerskin))
504                 self.skin = stof(self.playerskin);
505
506         W_WeaponFrame();
507
508         if (self.button4)
509         {
510                 if (self.viewzoom > 0.4)
511                         self.viewzoom = max (0.4, self.viewzoom - frametime * 2);
512         }
513         else if (self.viewzoom < 1.0)
514                 self.viewzoom = min (1.0, self.viewzoom + frametime);
515
516
517         if (self.button2)
518                 PlayerJump ();
519         else
520                 self.flags = self.flags | FL_JUMPRELEASED;
521
522
523         player_powerups();
524         player_regen();
525         player_anim();
526
527         self.angles_y=self.v_angle_y + 90;   // temp
528
529         WaterMove ();
530         if (self.waterlevel == 2)
531                 CheckWaterJump ();
532
533         //if (TetrisPreFrame()) return;
534 }
535
536 /*
537 =============
538 PlayerPostThink
539
540 Called every frame for each client after the physics are run
541 =============
542 */
543 void PlayerPostThink (void)
544 {
545         float soundrandom;
546         if (BotPostFrame())
547                 return;
548         if (self.health > 0)
549         if (self.impulse)
550                 ImpulseCommands ();
551         if (intermission_running)
552                 return;         // intermission or finale
553
554         // VorteX: landing on floor, landing damage etc.
555         // LordHavoc: removed 'big fall' death code that VorteX added
556         if (self.flags & FL_ONGROUND)
557         {
558                 if (self.jump_flag < -100 && !self.watertype == CONTENT_WATER) // HitGround
559                 {
560                         soundrandom = random() * 4;
561                         if (soundrandom < 1)
562                                 sound (self, CHAN_BODY, "misc/hitground1.wav", 1, ATTN_NORM);
563                         else if (soundrandom < 2)
564                                 sound (self, CHAN_BODY, "misc/hitground2.wav", 1, ATTN_NORM);
565                         else if (soundrandom < 3)
566                                 sound (self, CHAN_BODY, "misc/hitground3.wav", 1, ATTN_NORM);
567                         else if (soundrandom < 4)
568                                 sound (self, CHAN_BODY, "misc/hitground4.wav", 1, ATTN_NORM);
569                         if (self.jump_flag < -650) // landing damage
570                         {
571                                 local float dm;
572                                 dm = bound(0, 0.1*(fabs(self.jump_flag) - 600), 5);
573                                 Damage (self, world, world, dm, DEATH_FALL, '0 0 0', '0 0 0');
574                         }
575                         self.jump_flag = 0;
576                 }
577         }
578         else
579                 self.jump_flag = self.velocity_z;
580
581         //if (TetrisPostFrame()) return;
582 }