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