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