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