1 /*
2 Copyright (C) 1997-2001 Id Software, Inc.
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13 See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18
19 */
20
21 #include "g_local.h"
22 #include "m_player.h"
23
24
25
26 static edict_t *current_player;
27 static gclient_t *current_client;
28
29 static vec3_t forward, right, up;
30 float xyspeed;
31
32 float bobmove;
33 int bobcycle; // odd cycles are right foot going forward
34 float bobfracsin; // sin(bobfrac*M_PI)
35
36 /*
37 ===============
38 SV_CalcRoll
39
40 ===============
41 */
SV_CalcRoll(vec3_t angles,vec3_t velocity)42 float SV_CalcRoll (vec3_t angles, vec3_t velocity)
43 {
44 float sign;
45 float side;
46 float value;
47
48 side = DotProduct (velocity, right);
49 sign = side < 0 ? -1 : 1;
50 side = fabs(side);
51
52 value = sv_rollangle->value;
53
54 if (side < sv_rollspeed->value)
55 side = side * value / sv_rollspeed->value;
56 else
57 side = value;
58
59 return side*sign;
60
61 }
62
63
64 /*
65 ===============
66 P_DamageFeedback
67
68 Handles color blends and view kicks
69 ===============
70 */
P_DamageFeedback(edict_t * player)71 void P_DamageFeedback (edict_t *player)
72 {
73 gclient_t *client;
74 float side;
75 float realcount, count, kick;
76 vec3_t v;
77 int r, l;
78 static vec3_t power_color = {0.0, 1.0, 0.0};
79 static vec3_t acolor = {1.0, 1.0, 1.0};
80 static vec3_t bcolor = {1.0, 0.0, 0.0};
81
82 client = player->client;
83
84 // flash the backgrounds behind the status numbers
85 client->ps.stats[STAT_FLASHES] = 0;
86 if (client->damage_blood)
87 client->ps.stats[STAT_FLASHES] |= 1;
88 if (client->damage_armor && !(player->flags & FL_GODMODE) && (client->invincible_framenum <= level.framenum))
89 client->ps.stats[STAT_FLASHES] |= 2;
90
91 // total points of damage shot at the player this frame
92 count = (client->damage_blood + client->damage_armor + client->damage_parmor);
93 if (count == 0)
94 return; // didn't take any damage
95
96 // start a pain animation if still in the player model
97 if (client->anim_priority < ANIM_PAIN && player->s.modelindex == 255)
98 {
99 static int i;
100
101 client->anim_priority = ANIM_PAIN;
102 if (client->ps.pmove.pm_flags & PMF_DUCKED)
103 {
104 player->s.frame = FRAME_crpain1-1;
105 client->anim_end = FRAME_crpain4;
106 }
107 else
108 {
109 i = (i+1)%3;
110 switch (i)
111 {
112 case 0:
113 player->s.frame = FRAME_pain101-1;
114 client->anim_end = FRAME_pain104;
115 break;
116 case 1:
117 player->s.frame = FRAME_pain201-1;
118 client->anim_end = FRAME_pain204;
119 break;
120 case 2:
121 player->s.frame = FRAME_pain301-1;
122 client->anim_end = FRAME_pain304;
123 break;
124 }
125 }
126 }
127
128 realcount = count;
129 if (count < 10)
130 count = 10; // always make a visible effect
131
132 // play an apropriate pain sound
133 if ((level.time > player->pain_debounce_time) && !(player->flags & FL_GODMODE) && (client->invincible_framenum <= level.framenum))
134 {
135 r = 1 + (rand()&1);
136 player->pain_debounce_time = level.time + 0.7;
137 if (player->health < 25)
138 l = 25;
139 else if (player->health < 50)
140 l = 50;
141 else if (player->health < 75)
142 l = 75;
143 else
144 l = 100;
145 gi.sound (player, CHAN_VOICE, gi.soundindex(va("*pain%i_%i.wav", l, r)), 1, ATTN_NORM, 0);
146 }
147
148 // the total alpha of the blend is always proportional to count
149 if (client->damage_alpha < 0)
150 client->damage_alpha = 0;
151 client->damage_alpha += count*0.01;
152 if (client->damage_alpha < 0.2)
153 client->damage_alpha = 0.2;
154 if (client->damage_alpha > 0.6)
155 client->damage_alpha = 0.6; // don't go too saturated
156
157 // the color of the blend will vary based on how much was absorbed
158 // by different armors
159 VectorClear (v);
160 if (client->damage_parmor)
161 VectorMA (v, (float)client->damage_parmor/realcount, power_color, v);
162 if (client->damage_armor)
163 VectorMA (v, (float)client->damage_armor/realcount, acolor, v);
164 if (client->damage_blood)
165 VectorMA (v, (float)client->damage_blood/realcount, bcolor, v);
166 VectorCopy (v, client->damage_blend);
167
168
169 //
170 // calculate view angle kicks
171 //
172 kick = abs(client->damage_knockback);
173 if (kick && player->health > 0) // kick of 0 means no view adjust at all
174 {
175 kick = kick * 100 / player->health;
176
177 if (kick < count*0.5)
178 kick = count*0.5;
179 if (kick > 50)
180 kick = 50;
181
182 VectorSubtract (client->damage_from, player->s.origin, v);
183 VectorNormalize (v);
184
185 side = DotProduct (v, right);
186 client->v_dmg_roll = kick*side*0.3;
187
188 side = -DotProduct (v, forward);
189 client->v_dmg_pitch = kick*side*0.3;
190
191 client->v_dmg_time = level.time + DAMAGE_TIME;
192 }
193
194 //
195 // clear totals
196 //
197 client->damage_blood = 0;
198 client->damage_armor = 0;
199 client->damage_parmor = 0;
200 client->damage_knockback = 0;
201 }
202
203
204
205
206 /*
207 ===============
208 SV_CalcViewOffset
209
210 Auto pitching on slopes?
211
212 fall from 128: 400 = 160000
213 fall from 256: 580 = 336400
214 fall from 384: 720 = 518400
215 fall from 512: 800 = 640000
216 fall from 640: 960 =
217
218 damage = deltavelocity*deltavelocity * 0.0001
219
220 ===============
221 */
SV_CalcViewOffset(edict_t * ent)222 void SV_CalcViewOffset (edict_t *ent)
223 {
224 float *angles;
225 float bob;
226 float ratio;
227 float delta;
228 vec3_t v;
229
230
231 //===================================
232
233 // base angles
234 angles = ent->client->ps.kick_angles;
235
236 // if dead, fix the angle and don't add any kick
237 if (ent->deadflag)
238 {
239 VectorClear (angles);
240
241 ent->client->ps.viewangles[ROLL] = 40;
242 ent->client->ps.viewangles[PITCH] = -15;
243 ent->client->ps.viewangles[YAW] = ent->client->killer_yaw;
244 }
245 else
246 {
247 // add angles based on weapon kick
248
249 VectorCopy (ent->client->kick_angles, angles);
250
251 // add angles based on damage kick
252
253 ratio = (ent->client->v_dmg_time - level.time) / DAMAGE_TIME;
254 if (ratio < 0)
255 {
256 ratio = 0;
257 ent->client->v_dmg_pitch = 0;
258 ent->client->v_dmg_roll = 0;
259 }
260 angles[PITCH] += ratio * ent->client->v_dmg_pitch;
261 angles[ROLL] += ratio * ent->client->v_dmg_roll;
262
263 // add pitch based on fall kick
264
265 ratio = (ent->client->fall_time - level.time) / FALL_TIME;
266 if (ratio < 0)
267 ratio = 0;
268 angles[PITCH] += ratio * ent->client->fall_value;
269
270 // add angles based on velocity
271
272 delta = DotProduct (ent->velocity, forward);
273 angles[PITCH] += delta*run_pitch->value;
274
275 delta = DotProduct (ent->velocity, right);
276 angles[ROLL] += delta*run_roll->value;
277
278 // add angles based on bob
279
280 delta = bobfracsin * bob_pitch->value * xyspeed;
281 if (ent->client->ps.pmove.pm_flags & PMF_DUCKED)
282 delta *= 6; // crouching
283 angles[PITCH] += delta;
284 delta = bobfracsin * bob_roll->value * xyspeed;
285 if (ent->client->ps.pmove.pm_flags & PMF_DUCKED)
286 delta *= 6; // crouching
287 if (bobcycle & 1)
288 delta = -delta;
289 angles[ROLL] += delta;
290 }
291
292 //===================================
293
294 // base origin
295
296 VectorClear (v);
297
298 // add view height
299
300 v[2] += ent->viewheight;
301
302 // add fall height
303
304 ratio = (ent->client->fall_time - level.time) / FALL_TIME;
305 if (ratio < 0)
306 ratio = 0;
307 v[2] -= ratio * ent->client->fall_value * 0.4;
308
309 // add bob height
310
311 bob = bobfracsin * xyspeed * bob_up->value;
312 if (bob > 6)
313 bob = 6;
314 //gi.DebugGraph (bob *2, 255);
315 v[2] += bob;
316
317 // add kick offset
318
319 VectorAdd (v, ent->client->kick_origin, v);
320
321 // absolutely bound offsets
322 // so the view can never be outside the player box
323
324 if (v[0] < -14)
325 v[0] = -14;
326 else if (v[0] > 14)
327 v[0] = 14;
328 if (v[1] < -14)
329 v[1] = -14;
330 else if (v[1] > 14)
331 v[1] = 14;
332 if (v[2] < -22)
333 v[2] = -22;
334 else if (v[2] > 30)
335 v[2] = 30;
336
337 VectorCopy (v, ent->client->ps.viewoffset);
338 }
339
340 /*
341 ==============
342 SV_CalcGunOffset
343 ==============
344 */
SV_CalcGunOffset(edict_t * ent)345 void SV_CalcGunOffset (edict_t *ent)
346 {
347 int i;
348 float delta;
349
350 // gun angles from bobbing
351 ent->client->ps.gunangles[ROLL] = xyspeed * bobfracsin * 0.005;
352 ent->client->ps.gunangles[YAW] = xyspeed * bobfracsin * 0.01;
353 if (bobcycle & 1)
354 {
355 ent->client->ps.gunangles[ROLL] = -ent->client->ps.gunangles[ROLL];
356 ent->client->ps.gunangles[YAW] = -ent->client->ps.gunangles[YAW];
357 }
358
359 ent->client->ps.gunangles[PITCH] = xyspeed * bobfracsin * 0.005;
360
361 // gun angles from delta movement
362 for (i=0 ; i<3 ; i++)
363 {
364 delta = ent->client->oldviewangles[i] - ent->client->ps.viewangles[i];
365 if (delta > 180)
366 delta -= 360;
367 if (delta < -180)
368 delta += 360;
369 if (delta > 45)
370 delta = 45;
371 if (delta < -45)
372 delta = -45;
373 if (i == YAW)
374 ent->client->ps.gunangles[ROLL] += 0.1*delta;
375 ent->client->ps.gunangles[i] += 0.2 * delta;
376 }
377
378 // gun height
379 VectorClear (ent->client->ps.gunoffset);
380 // ent->ps->gunorigin[2] += bob;
381
382 // gun_x / gun_y / gun_z are development tools
383 for (i=0 ; i<3 ; i++)
384 {
385 ent->client->ps.gunoffset[i] += forward[i]*(gun_y->value);
386 ent->client->ps.gunoffset[i] += right[i]*gun_x->value;
387 ent->client->ps.gunoffset[i] += up[i]* (-gun_z->value);
388 }
389 }
390
391
392 /*
393 =============
394 SV_AddBlend
395 =============
396 */
SV_AddBlend(float r,float g,float b,float a,float * v_blend)397 void SV_AddBlend (float r, float g, float b, float a, float *v_blend)
398 {
399 float a2, a3;
400
401 if (a <= 0)
402 return;
403 a2 = v_blend[3] + (1-v_blend[3])*a; // new total alpha
404 a3 = v_blend[3]/a2; // fraction of color from old
405
406 v_blend[0] = v_blend[0]*a3 + r*(1-a3);
407 v_blend[1] = v_blend[1]*a3 + g*(1-a3);
408 v_blend[2] = v_blend[2]*a3 + b*(1-a3);
409 v_blend[3] = a2;
410 }
411
412
413 /*
414 =============
415 SV_CalcBlend
416 =============
417 */
SV_CalcBlend(edict_t * ent)418 void SV_CalcBlend (edict_t *ent)
419 {
420 int contents;
421 vec3_t vieworg;
422 int remaining;
423
424 ent->client->ps.blend[0] = ent->client->ps.blend[1] =
425 ent->client->ps.blend[2] = ent->client->ps.blend[3] = 0;
426
427 // add for contents
428 VectorAdd (ent->s.origin, ent->client->ps.viewoffset, vieworg);
429 contents = gi.pointcontents (vieworg);
430 if (contents & (CONTENTS_LAVA|CONTENTS_SLIME|CONTENTS_WATER) )
431 ent->client->ps.rdflags |= RDF_UNDERWATER;
432 else
433 ent->client->ps.rdflags &= ~RDF_UNDERWATER;
434
435 if (contents & (CONTENTS_SOLID|CONTENTS_LAVA))
436 SV_AddBlend (1.0, 0.3, 0.0, 0.6, ent->client->ps.blend);
437 else if (contents & CONTENTS_SLIME)
438 SV_AddBlend (0.0, 0.1, 0.05, 0.6, ent->client->ps.blend);
439 else if (contents & CONTENTS_WATER)
440 SV_AddBlend (0.5, 0.3, 0.2, 0.4, ent->client->ps.blend);
441
442 // add for powerups
443 if (ent->client->quad_framenum > level.framenum)
444 {
445 remaining = ent->client->quad_framenum - level.framenum;
446 if (remaining == 30) // beginning to fade
447 gi.sound(ent, CHAN_ITEM, gi.soundindex("items/damage2.wav"), 1, ATTN_NORM, 0);
448 if (remaining > 30 || (remaining & 4) )
449 SV_AddBlend (0, 0, 1, 0.08, ent->client->ps.blend);
450 }
451 else if (ent->client->invincible_framenum > level.framenum)
452 {
453 remaining = ent->client->invincible_framenum - level.framenum;
454 if (remaining == 30) // beginning to fade
455 gi.sound(ent, CHAN_ITEM, gi.soundindex("items/protect2.wav"), 1, ATTN_NORM, 0);
456 if (remaining > 30 || (remaining & 4) )
457 SV_AddBlend (1, 1, 0, 0.08, ent->client->ps.blend);
458 }
459 else if (ent->client->enviro_framenum > level.framenum)
460 {
461 remaining = ent->client->enviro_framenum - level.framenum;
462 if (remaining == 30) // beginning to fade
463 gi.sound(ent, CHAN_ITEM, gi.soundindex("items/airout.wav"), 1, ATTN_NORM, 0);
464 if (remaining > 30 || (remaining & 4) )
465 SV_AddBlend (0, 1, 0, 0.08, ent->client->ps.blend);
466 }
467 else if (ent->client->breather_framenum > level.framenum)
468 {
469 remaining = ent->client->breather_framenum - level.framenum;
470 if (remaining == 30) // beginning to fade
471 gi.sound(ent, CHAN_ITEM, gi.soundindex("items/airout.wav"), 1, ATTN_NORM, 0);
472 if (remaining > 30 || (remaining & 4) )
473 SV_AddBlend (0.4, 1, 0.4, 0.04, ent->client->ps.blend);
474 }
475
476 // add for damage
477 if (ent->client->damage_alpha > 0)
478 SV_AddBlend (ent->client->damage_blend[0],ent->client->damage_blend[1]
479 ,ent->client->damage_blend[2], ent->client->damage_alpha, ent->client->ps.blend);
480
481 if (ent->client->bonus_alpha > 0)
482 SV_AddBlend (0.85, 0.7, 0.3, ent->client->bonus_alpha, ent->client->ps.blend);
483
484 // drop the damage value
485 ent->client->damage_alpha -= 0.06;
486 if (ent->client->damage_alpha < 0)
487 ent->client->damage_alpha = 0;
488
489 // drop the bonus value
490 ent->client->bonus_alpha -= 0.1;
491 if (ent->client->bonus_alpha < 0)
492 ent->client->bonus_alpha = 0;
493 }
494
495
496 /*
497 =================
498 P_FallingDamage
499 =================
500 */
P_FallingDamage(edict_t * ent)501 void P_FallingDamage (edict_t *ent)
502 {
503 float delta;
504 int damage;
505 vec3_t dir;
506
507 if (ent->s.modelindex != 255)
508 return; // not in the player model
509
510 if (ent->movetype == MOVETYPE_NOCLIP)
511 return;
512
513 if ((ent->client->oldvelocity[2] < 0) && (ent->velocity[2] > ent->client->oldvelocity[2]) && (!ent->groundentity))
514 {
515 delta = ent->client->oldvelocity[2];
516 }
517 else
518 {
519 if (!ent->groundentity)
520 return;
521 delta = ent->velocity[2] - ent->client->oldvelocity[2];
522 }
523 delta = delta*delta * 0.0001;
524
525 // never take falling damage if completely underwater
526 if (ent->waterlevel == 3)
527 return;
528 if (ent->waterlevel == 2)
529 delta *= 0.25;
530 if (ent->waterlevel == 1)
531 delta *= 0.5;
532
533 if (delta < 1)
534 return;
535
536 if (delta < 15)
537 {
538 ent->s.event = EV_FOOTSTEP;
539 return;
540 }
541
542 ent->client->fall_value = delta*0.5;
543 if (ent->client->fall_value > 40)
544 ent->client->fall_value = 40;
545 ent->client->fall_time = level.time + FALL_TIME;
546
547 if (delta > 30)
548 {
549 if (ent->health > 0)
550 {
551 if (delta >= 55)
552 ent->s.event = EV_FALLFAR;
553 else
554 ent->s.event = EV_FALL;
555 }
556 ent->pain_debounce_time = level.time; // no normal pain sound
557 damage = (delta-30)/2;
558 if (damage < 1)
559 damage = 1;
560 VectorSet (dir, 0, 0, 1);
561
562 if (!deathmatch->value || !((int)dmflags->value & DF_NO_FALLING) )
563 T_Damage (ent, world, world, dir, ent->s.origin, vec3_origin, damage, 0, 0, MOD_FALLING);
564 }
565 else
566 {
567 ent->s.event = EV_FALLSHORT;
568 return;
569 }
570 }
571
572
573
574 /*
575 =============
576 P_WorldEffects
577 =============
578 */
P_WorldEffects(void)579 void P_WorldEffects (void)
580 {
581 qboolean breather;
582 qboolean envirosuit;
583 int waterlevel, old_waterlevel;
584
585 if (current_player->movetype == MOVETYPE_NOCLIP)
586 {
587 current_player->air_finished = level.time + 12; // don't need air
588 return;
589 }
590
591 waterlevel = current_player->waterlevel;
592 old_waterlevel = current_client->old_waterlevel;
593 current_client->old_waterlevel = waterlevel;
594
595 breather = current_client->breather_framenum > level.framenum;
596 envirosuit = current_client->enviro_framenum > level.framenum;
597
598 //
599 // if just entered a water volume, play a sound
600 //
601 if (!old_waterlevel && waterlevel)
602 {
603 PlayerNoise(current_player, current_player->s.origin, PNOISE_SELF);
604 if (current_player->watertype & CONTENTS_LAVA)
605 gi.sound (current_player, CHAN_BODY, gi.soundindex("player/lava_in.wav"), 1, ATTN_NORM, 0);
606 else if (current_player->watertype & CONTENTS_SLIME)
607 gi.sound (current_player, CHAN_BODY, gi.soundindex("player/watr_in.wav"), 1, ATTN_NORM, 0);
608 else if (current_player->watertype & CONTENTS_WATER)
609 gi.sound (current_player, CHAN_BODY, gi.soundindex("player/watr_in.wav"), 1, ATTN_NORM, 0);
610 current_player->flags |= FL_INWATER;
611
612 // clear damage_debounce, so the pain sound will play immediately
613 current_player->damage_debounce_time = level.time - 1;
614 }
615
616 //
617 // if just completely exited a water volume, play a sound
618 //
619 if (old_waterlevel && ! waterlevel)
620 {
621 PlayerNoise(current_player, current_player->s.origin, PNOISE_SELF);
622 gi.sound (current_player, CHAN_BODY, gi.soundindex("player/watr_out.wav"), 1, ATTN_NORM, 0);
623 current_player->flags &= ~FL_INWATER;
624 }
625
626 //
627 // check for head just going under water
628 //
629 if (old_waterlevel != 3 && waterlevel == 3)
630 {
631 gi.sound (current_player, CHAN_BODY, gi.soundindex("player/watr_un.wav"), 1, ATTN_NORM, 0);
632 }
633
634 //
635 // check for head just coming out of water
636 //
637 if (old_waterlevel == 3 && waterlevel != 3)
638 {
639 if (current_player->air_finished < level.time)
640 { // gasp for air
641 gi.sound (current_player, CHAN_VOICE, gi.soundindex("player/gasp1.wav"), 1, ATTN_NORM, 0);
642 PlayerNoise(current_player, current_player->s.origin, PNOISE_SELF);
643 }
644 else if (current_player->air_finished < level.time + 11)
645 { // just break surface
646 gi.sound (current_player, CHAN_VOICE, gi.soundindex("player/gasp2.wav"), 1, ATTN_NORM, 0);
647 }
648 }
649
650 //
651 // check for drowning
652 //
653 if (waterlevel == 3)
654 {
655 // breather or envirosuit give air
656 if (breather || envirosuit)
657 {
658 current_player->air_finished = level.time + 10;
659
660 if (((int)(current_client->breather_framenum - level.framenum) % 25) == 0)
661 {
662 if (!current_client->breather_sound)
663 gi.sound (current_player, CHAN_AUTO, gi.soundindex("player/u_breath1.wav"), 1, ATTN_NORM, 0);
664 else
665 gi.sound (current_player, CHAN_AUTO, gi.soundindex("player/u_breath2.wav"), 1, ATTN_NORM, 0);
666 current_client->breather_sound ^= 1;
667 PlayerNoise(current_player, current_player->s.origin, PNOISE_SELF);
668 //FIXME: release a bubble?
669 }
670 }
671
672 // if out of air, start drowning
673 if (current_player->air_finished < level.time)
674 { // drown!
675 if (current_player->client->next_drown_time < level.time
676 && current_player->health > 0)
677 {
678 current_player->client->next_drown_time = level.time + 1;
679
680 // take more damage the longer underwater
681 current_player->dmg += 2;
682 if (current_player->dmg > 15)
683 current_player->dmg = 15;
684
685 // play a gurp sound instead of a normal pain sound
686 if (current_player->health <= current_player->dmg)
687 gi.sound (current_player, CHAN_VOICE, gi.soundindex("player/drown1.wav"), 1, ATTN_NORM, 0);
688 else if (rand()&1)
689 gi.sound (current_player, CHAN_VOICE, gi.soundindex("*gurp1.wav"), 1, ATTN_NORM, 0);
690 else
691 gi.sound (current_player, CHAN_VOICE, gi.soundindex("*gurp2.wav"), 1, ATTN_NORM, 0);
692
693 current_player->pain_debounce_time = level.time;
694
695 T_Damage (current_player, world, world, vec3_origin, current_player->s.origin, vec3_origin, current_player->dmg, 0, DAMAGE_NO_ARMOR, MOD_WATER);
696 }
697 }
698 }
699 else
700 {
701 current_player->air_finished = level.time + 12;
702 current_player->dmg = 2;
703 }
704
705 //
706 // check for sizzle damage
707 //
708 if (waterlevel && (current_player->watertype&(CONTENTS_LAVA|CONTENTS_SLIME)) )
709 {
710 if (current_player->watertype & CONTENTS_LAVA)
711 {
712 if (current_player->health > 0
713 && current_player->pain_debounce_time <= level.time
714 && current_client->invincible_framenum < level.framenum)
715 {
716 if (rand()&1)
717 gi.sound (current_player, CHAN_VOICE, gi.soundindex("player/burn1.wav"), 1, ATTN_NORM, 0);
718 else
719 gi.sound (current_player, CHAN_VOICE, gi.soundindex("player/burn2.wav"), 1, ATTN_NORM, 0);
720 current_player->pain_debounce_time = level.time + 1;
721 }
722
723 if (envirosuit) // take 1/3 damage with envirosuit
724 T_Damage (current_player, world, world, vec3_origin, current_player->s.origin, vec3_origin, 1*waterlevel, 0, 0, MOD_LAVA);
725 else
726 T_Damage (current_player, world, world, vec3_origin, current_player->s.origin, vec3_origin, 3*waterlevel, 0, 0, MOD_LAVA);
727 }
728
729 if (current_player->watertype & CONTENTS_SLIME)
730 {
731 if (!envirosuit)
732 { // no damage from slime with envirosuit
733 T_Damage (current_player, world, world, vec3_origin, current_player->s.origin, vec3_origin, 1*waterlevel, 0, 0, MOD_SLIME);
734 }
735 }
736 }
737 }
738
739
740 /*
741 ===============
742 G_SetClientEffects
743 ===============
744 */
G_SetClientEffects(edict_t * ent)745 void G_SetClientEffects (edict_t *ent)
746 {
747 int pa_type;
748 int remaining;
749
750 ent->s.effects = 0;
751 ent->s.renderfx = 0;
752
753 if (ent->health <= 0 || level.intermissiontime)
754 return;
755
756 if (ent->powerarmor_time > level.time)
757 {
758 pa_type = PowerArmorType (ent);
759 if (pa_type == POWER_ARMOR_SCREEN)
760 {
761 ent->s.effects |= EF_POWERSCREEN;
762 }
763 else if (pa_type == POWER_ARMOR_SHIELD)
764 {
765 ent->s.effects |= EF_COLOR_SHELL;
766 ent->s.renderfx |= RF_SHELL_GREEN;
767 }
768 }
769
770 if (ent->client->quad_framenum > level.framenum)
771 {
772 remaining = ent->client->quad_framenum - level.framenum;
773 if (remaining > 30 || (remaining & 4) )
774 ent->s.effects |= EF_QUAD;
775 }
776
777 if (ent->client->invincible_framenum > level.framenum)
778 {
779 remaining = ent->client->invincible_framenum - level.framenum;
780 if (remaining > 30 || (remaining & 4) )
781 ent->s.effects |= EF_PENT;
782 }
783
784 // show cheaters!!!
785 if (ent->flags & FL_GODMODE)
786 {
787 ent->s.effects |= EF_COLOR_SHELL;
788 ent->s.renderfx |= (RF_SHELL_RED|RF_SHELL_GREEN|RF_SHELL_BLUE);
789 }
790 }
791
792
793 /*
794 ===============
795 G_SetClientEvent
796 ===============
797 */
G_SetClientEvent(edict_t * ent)798 void G_SetClientEvent (edict_t *ent)
799 {
800 if (ent->s.event)
801 return;
802
803 if ( ent->groundentity && xyspeed > 225)
804 {
805 if ( (int)(current_client->bobtime+bobmove) != bobcycle )
806 ent->s.event = EV_FOOTSTEP;
807 }
808 }
809
810 /*
811 ===============
812 G_SetClientSound
813 ===============
814 */
G_SetClientSound(edict_t * ent)815 void G_SetClientSound (edict_t *ent)
816 {
817 char *weap;
818
819 if (ent->client->pers.game_helpchanged != game.helpchanged)
820 {
821 ent->client->pers.game_helpchanged = game.helpchanged;
822 ent->client->pers.helpchanged = 1;
823 }
824
825 // help beep (no more than three times)
826 if (ent->client->pers.helpchanged && ent->client->pers.helpchanged <= 3 && !(level.framenum&63) )
827 {
828 ent->client->pers.helpchanged++;
829 gi.sound (ent, CHAN_VOICE, gi.soundindex ("misc/pc_up.wav"), 1, ATTN_STATIC, 0);
830 }
831
832
833 if (ent->client->pers.weapon)
834 weap = ent->client->pers.weapon->classname;
835 else
836 weap = "";
837
838 if (ent->waterlevel && (ent->watertype&(CONTENTS_LAVA|CONTENTS_SLIME)) )
839 ent->s.sound = snd_fry;
840 else if (strcmp(weap, "weapon_railgun") == 0)
841 ent->s.sound = gi.soundindex("weapons/rg_hum.wav");
842 else if (strcmp(weap, "weapon_bfg") == 0)
843 ent->s.sound = gi.soundindex("weapons/bfg_hum.wav");
844 else if (ent->client->weapon_sound)
845 ent->s.sound = ent->client->weapon_sound;
846 else
847 ent->s.sound = 0;
848 }
849
850 /*
851 ===============
852 G_SetClientFrame
853 ===============
854 */
G_SetClientFrame(edict_t * ent)855 void G_SetClientFrame (edict_t *ent)
856 {
857 gclient_t *client;
858 qboolean duck, run;
859
860 if (ent->s.modelindex != 255)
861 return; // not in the player model
862
863 client = ent->client;
864
865 if (client->ps.pmove.pm_flags & PMF_DUCKED)
866 duck = true;
867 else
868 duck = false;
869 if (xyspeed)
870 run = true;
871 else
872 run = false;
873
874 // check for stand/duck and stop/go transitions
875 if (duck != client->anim_duck && client->anim_priority < ANIM_DEATH)
876 goto newanim;
877 if (run != client->anim_run && client->anim_priority == ANIM_BASIC)
878 goto newanim;
879 if (!ent->groundentity && client->anim_priority <= ANIM_WAVE)
880 goto newanim;
881
882 if(client->anim_priority == ANIM_REVERSE)
883 {
884 if(ent->s.frame > client->anim_end)
885 {
886 ent->s.frame--;
887 return;
888 }
889 }
890 else if (ent->s.frame < client->anim_end)
891 { // continue an animation
892 ent->s.frame++;
893 return;
894 }
895
896 if (client->anim_priority == ANIM_DEATH)
897 return; // stay there
898 if (client->anim_priority == ANIM_JUMP)
899 {
900 if (!ent->groundentity)
901 return; // stay there
902 ent->client->anim_priority = ANIM_WAVE;
903 ent->s.frame = FRAME_jump3;
904 ent->client->anim_end = FRAME_jump6;
905 return;
906 }
907
908 newanim:
909 // return to either a running or standing frame
910 client->anim_priority = ANIM_BASIC;
911 client->anim_duck = duck;
912 client->anim_run = run;
913
914 if (!ent->groundentity)
915 {
916 client->anim_priority = ANIM_JUMP;
917 if (ent->s.frame != FRAME_jump2)
918 ent->s.frame = FRAME_jump1;
919 client->anim_end = FRAME_jump2;
920 }
921 else if (run)
922 { // running
923 if (duck)
924 {
925 ent->s.frame = FRAME_crwalk1;
926 client->anim_end = FRAME_crwalk6;
927 }
928 else
929 {
930 ent->s.frame = FRAME_run1;
931 client->anim_end = FRAME_run6;
932 }
933 }
934 else
935 { // standing
936 if (duck)
937 {
938 ent->s.frame = FRAME_crstnd01;
939 client->anim_end = FRAME_crstnd19;
940 }
941 else
942 {
943 ent->s.frame = FRAME_stand01;
944 client->anim_end = FRAME_stand40;
945 }
946 }
947 }
948
949
950 /*
951 =================
952 ClientEndServerFrame
953
954 Called for each player at the end of the server frame
955 and right after spawning
956 =================
957 */
ClientEndServerFrame(edict_t * ent)958 void ClientEndServerFrame (edict_t *ent)
959 {
960 float bobtime;
961 int i;
962
963 current_player = ent;
964 current_client = ent->client;
965
966 //
967 // If the origin or velocity have changed since ClientThink(),
968 // update the pmove values. This will happen when the client
969 // is pushed by a bmodel or kicked by an explosion.
970 //
971 // If it wasn't updated here, the view position would lag a frame
972 // behind the body position when pushed -- "sinking into plats"
973 //
974 for (i=0 ; i<3 ; i++)
975 {
976 current_client->ps.pmove.origin[i] = ent->s.origin[i]*8.0;
977 current_client->ps.pmove.velocity[i] = ent->velocity[i]*8.0;
978 }
979
980 //
981 // If the end of unit layout is displayed, don't give
982 // the player any normal movement attributes
983 //
984 if (level.intermissiontime)
985 {
986 // FIXME: add view drifting here?
987 current_client->ps.blend[3] = 0;
988 current_client->ps.fov = 90;
989 G_SetStats (ent);
990 return;
991 }
992
993 AngleVectors (ent->client->v_angle, forward, right, up);
994
995 // burn from lava, etc
996 P_WorldEffects ();
997
998 //
999 // set model angles from view angles so other things in
1000 // the world can tell which direction you are looking
1001 //
1002 if (ent->client->v_angle[PITCH] > 180)
1003 ent->s.angles[PITCH] = (-360 + ent->client->v_angle[PITCH])/3;
1004 else
1005 ent->s.angles[PITCH] = ent->client->v_angle[PITCH]/3;
1006 ent->s.angles[YAW] = ent->client->v_angle[YAW];
1007 ent->s.angles[ROLL] = 0;
1008 ent->s.angles[ROLL] = SV_CalcRoll (ent->s.angles, ent->velocity)*4;
1009
1010 //
1011 // calculate speed and cycle to be used for
1012 // all cyclic walking effects
1013 //
1014 xyspeed = sqrt(ent->velocity[0]*ent->velocity[0] + ent->velocity[1]*ent->velocity[1]);
1015
1016 if (xyspeed < 5)
1017 {
1018 bobmove = 0;
1019 current_client->bobtime = 0; // start at beginning of cycle again
1020 }
1021 else if (ent->groundentity)
1022 { // so bobbing only cycles when on ground
1023 if (xyspeed > 210)
1024 bobmove = 0.25;
1025 else if (xyspeed > 100)
1026 bobmove = 0.125;
1027 else
1028 bobmove = 0.0625;
1029 }
1030
1031 bobtime = (current_client->bobtime += bobmove);
1032
1033 if (current_client->ps.pmove.pm_flags & PMF_DUCKED)
1034 bobtime *= 4;
1035
1036 bobcycle = (int)bobtime;
1037 bobfracsin = fabs(sin(bobtime*M_PI));
1038
1039 // detect hitting the floor
1040 P_FallingDamage (ent);
1041
1042 // apply all the damage taken this frame
1043 P_DamageFeedback (ent);
1044
1045 // determine the view offsets
1046 SV_CalcViewOffset (ent);
1047
1048 // determine the gun offsets
1049 SV_CalcGunOffset (ent);
1050
1051 // determine the full screen color blend
1052 // must be after viewoffset, so eye contents can be
1053 // accurately determined
1054 // FIXME: with client prediction, the contents
1055 // should be determined by the client
1056 SV_CalcBlend (ent);
1057
1058 // chase cam stuff
1059 if (ent->client->resp.spectator)
1060 G_SetSpectatorStats(ent);
1061 else
1062 G_SetStats (ent);
1063 G_CheckChaseStats(ent);
1064
1065 G_SetClientEvent (ent);
1066
1067 G_SetClientEffects (ent);
1068
1069 G_SetClientSound (ent);
1070
1071 G_SetClientFrame (ent);
1072
1073 VectorCopy (ent->velocity, ent->client->oldvelocity);
1074 VectorCopy (ent->client->ps.viewangles, ent->client->oldviewangles);
1075
1076 // clear weapon kicks
1077 VectorClear (ent->client->kick_origin);
1078 VectorClear (ent->client->kick_angles);
1079
1080 // if the scoreboard is up, update it
1081 if (ent->client->showscores && !(level.framenum & 31) )
1082 {
1083 DeathmatchScoreboardMessage (ent, ent->enemy);
1084 gi.unicast (ent, false);
1085 }
1086 }
1087
1088