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 ent->client->ps.viewangles[ROLL] = 40;
223 ent->client->ps.viewangles[PITCH] = -15;
224 ent->client->ps.viewangles[YAW] = ent->client->killer_yaw;
225 }
226 else
227 {
228 // add angles based on weapon kick
229
230 VectorCopy (ent->client->kick_angles, angles);
231
232 // add angles based on damage kick
233
234 ratio = (ent->client->v_dmg_time - level.time) / DAMAGE_TIME;
235 if (ratio < 0)
236 {
237 ratio = 0;
238 ent->client->v_dmg_pitch = 0;
239 ent->client->v_dmg_roll = 0;
240 }
241 angles[PITCH] += ratio * ent->client->v_dmg_pitch;
242 angles[ROLL] += ratio * ent->client->v_dmg_roll;
243
244 // add pitch based on fall kick
245
246 ratio = (ent->client->fall_time - level.time) / FALL_TIME;
247 if (ratio < 0)
248 ratio = 0;
249 angles[PITCH] += ratio * ent->client->fall_value;
250
251 // add angles based on velocity
252
253 delta = DotProduct (ent->velocity, forward);
254 angles[PITCH] += delta*run_pitch->value;
255
256 delta = DotProduct (ent->velocity, right);
257 angles[ROLL] += delta*run_roll->value;
258
259 // add angles based on bob
260
261 delta = bobfracsin * bob_pitch->value * xyspeed;
262 if (ent->client->ps.pmove.pm_flags & PMF_DUCKED)
263 delta *= 6; // crouching
264 angles[PITCH] += delta;
265 delta = bobfracsin * bob_roll->value * xyspeed;
266 if (ent->client->ps.pmove.pm_flags & PMF_DUCKED)
267 delta *= 6; // crouching
268 if (bobcycle & 1)
269 delta = -delta;
270 angles[ROLL] += delta;
271 }
272
273 //===================================
274
275 // base origin
276
277 VectorClear (v);
278
279 // add view height
280
281 v[2] += ent->viewheight;
282
283 // add fall height
284
285 ratio = (ent->client->fall_time - level.time) / FALL_TIME;
286 if (ratio < 0)
287 ratio = 0;
288 v[2] -= ratio * ent->client->fall_value * 0.4;
289
290 // add bob height
291
292 bob = bobfracsin * xyspeed * bob_up->value;
293 if (bob > 6)
294 bob = 6;
295 //gi.DebugGraph (bob *2, 255);
296 v[2] += bob;
297
298 // add kick offset
299
300 VectorAdd (v, ent->client->kick_origin, v);
301
302 // absolutely bound offsets
303 // so the view can never be outside the player box
304
305 if (v[0] < -14)
306 v[0] = -14;
307 else if (v[0] > 14)
308 v[0] = 14;
309 if (v[1] < -14)
310 v[1] = -14;
311 else if (v[1] > 14)
312 v[1] = 14;
313 if (v[2] < -22)
314 v[2] = -22;
315 else if (v[2] > 30)
316 v[2] = 30;
317
318 VectorCopy (v, ent->client->ps.viewoffset);
319 }
320
321 /*
322 ==============
323 SV_CalcGunOffset
324 ==============
325 */
SV_CalcGunOffset(edict_t * ent)326 void SV_CalcGunOffset (edict_t *ent)
327 {
328 int i;
329 float delta;
330
331 // gun angles from bobbing
332 ent->client->ps.gunangles[ROLL] = xyspeed * bobfracsin * 0.005;
333 ent->client->ps.gunangles[YAW] = xyspeed * bobfracsin * 0.01;
334 if (bobcycle & 1)
335 {
336 ent->client->ps.gunangles[ROLL] = -ent->client->ps.gunangles[ROLL];
337 ent->client->ps.gunangles[YAW] = -ent->client->ps.gunangles[YAW];
338 }
339
340 ent->client->ps.gunangles[PITCH] = xyspeed * bobfracsin * 0.005;
341
342 // gun angles from delta movement
343 for (i=0 ; i<3 ; i++)
344 {
345 delta = ent->client->oldviewangles[i] - ent->client->ps.viewangles[i];
346 if (delta > 180)
347 delta -= 360;
348 if (delta < -180)
349 delta += 360;
350 if (delta > 45)
351 delta = 45;
352 if (delta < -45)
353 delta = -45;
354 if (i == YAW)
355 ent->client->ps.gunangles[ROLL] += 0.1*delta;
356 ent->client->ps.gunangles[i] += 0.2 * delta;
357 }
358
359 // gun height
360 VectorClear (ent->client->ps.gunoffset);
361 // ent->ps->gunorigin[2] += bob;
362
363 // gun_x / gun_y / gun_z are development tools
364 for (i=0 ; i<3 ; i++)
365 {
366 ent->client->ps.gunoffset[i] += forward[i]*(gun_y->value);
367 ent->client->ps.gunoffset[i] += right[i]*gun_x->value;
368 ent->client->ps.gunoffset[i] += up[i]* (-gun_z->value);
369 }
370 }
371
372
373 /*
374 =============
375 SV_AddBlend
376 =============
377 */
SV_AddBlend(float r,float g,float b,float a,float * v_blend)378 void SV_AddBlend (float r, float g, float b, float a, float *v_blend)
379 {
380 float a2, a3;
381
382 if (a <= 0)
383 return;
384 a2 = v_blend[3] + (1-v_blend[3])*a; // new total alpha
385 a3 = v_blend[3]/a2; // fraction of color from old
386
387 v_blend[0] = v_blend[0]*a3 + r*(1-a3);
388 v_blend[1] = v_blend[1]*a3 + g*(1-a3);
389 v_blend[2] = v_blend[2]*a3 + b*(1-a3);
390 v_blend[3] = a2;
391 }
392
393
394 /*
395 =============
396 SV_CalcBlend
397 =============
398 */
SV_CalcBlend(edict_t * ent)399 void SV_CalcBlend (edict_t *ent)
400 {
401 int contents;
402 vec3_t vieworg;
403 int remaining;
404
405 ent->client->ps.blend[0] = ent->client->ps.blend[1] =
406 ent->client->ps.blend[2] = ent->client->ps.blend[3] = 0;
407
408 // add for contents
409 VectorAdd (ent->s.origin, ent->client->ps.viewoffset, vieworg);
410 contents = gi.pointcontents (vieworg);
411 if (contents & (CONTENTS_LAVA|CONTENTS_SLIME|CONTENTS_WATER) )
412 ent->client->ps.rdflags |= RDF_UNDERWATER;
413 else
414 ent->client->ps.rdflags &= ~RDF_UNDERWATER;
415
416 if (contents & (CONTENTS_SOLID|CONTENTS_LAVA))
417 SV_AddBlend (1.0, 0.3, 0.0, 0.6, ent->client->ps.blend);
418 else if (contents & CONTENTS_SLIME)
419 SV_AddBlend (0.0, 0.1, 0.05, 0.6, ent->client->ps.blend);
420 else if (contents & CONTENTS_WATER)
421 SV_AddBlend (0.5, 0.3, 0.2, 0.4, ent->client->ps.blend);
422
423 // add for powerups
424 if (ent->client->quad_framenum > level.framenum)
425 {
426 remaining = ent->client->quad_framenum - level.framenum;
427 if (remaining == 30) // beginning to fade
428 gi.sound(ent, CHAN_ITEM, gi.soundindex("items/damage2.wav"), 1, ATTN_NORM, 0);
429 if (remaining > 30 || (remaining & 4) )
430 SV_AddBlend (0, 0, 1, 0.08, ent->client->ps.blend);
431 }
432 else if (ent->client->invincible_framenum > level.framenum)
433 {
434 remaining = ent->client->invincible_framenum - level.framenum;
435 if (remaining == 30) // beginning to fade
436 gi.sound(ent, CHAN_ITEM, gi.soundindex("items/protect2.wav"), 1, ATTN_NORM, 0);
437 if (remaining > 30 || (remaining & 4) )
438 SV_AddBlend (1, 1, 0, 0.08, ent->client->ps.blend);
439 }
440 else if (ent->client->enviro_framenum > level.framenum)
441 {
442 remaining = ent->client->enviro_framenum - level.framenum;
443 if (remaining == 30) // beginning to fade
444 gi.sound(ent, CHAN_ITEM, gi.soundindex("items/airout.wav"), 1, ATTN_NORM, 0);
445 if (remaining > 30 || (remaining & 4) )
446 SV_AddBlend (0, 1, 0, 0.08, ent->client->ps.blend);
447 }
448 else if (ent->client->breather_framenum > level.framenum)
449 {
450 remaining = ent->client->breather_framenum - level.framenum;
451 if (remaining == 30) // beginning to fade
452 gi.sound(ent, CHAN_ITEM, gi.soundindex("items/airout.wav"), 1, ATTN_NORM, 0);
453 if (remaining > 30 || (remaining & 4) )
454 SV_AddBlend (0.4, 1, 0.4, 0.04, ent->client->ps.blend);
455 }
456
457 // add for damage
458 if (ent->client->damage_alpha > 0)
459 SV_AddBlend (ent->client->damage_blend[0],ent->client->damage_blend[1]
460 ,ent->client->damage_blend[2], ent->client->damage_alpha, ent->client->ps.blend);
461
462 if (ent->client->bonus_alpha > 0)
463 SV_AddBlend (0.85, 0.7, 0.3, ent->client->bonus_alpha, ent->client->ps.blend);
464
465 // drop the damage value
466 ent->client->damage_alpha -= 0.06;
467 if (ent->client->damage_alpha < 0)
468 ent->client->damage_alpha = 0;
469
470 // drop the bonus value
471 ent->client->bonus_alpha -= 0.1;
472 if (ent->client->bonus_alpha < 0)
473 ent->client->bonus_alpha = 0;
474 }
475
476
477 /*
478 =================
479 P_FallingDamage
480 =================
481 */
P_FallingDamage(edict_t * ent)482 void P_FallingDamage (edict_t *ent)
483 {
484 float delta;
485 int damage;
486 vec3_t dir;
487
488 if (ent->s.modelindex != 255)
489 return; // not in the player model
490
491 if (ent->movetype == MOVETYPE_NOCLIP)
492 return;
493
494 if ((ent->client->oldvelocity[2] < 0) && (ent->velocity[2] > ent->client->oldvelocity[2]) && (!ent->groundentity))
495 {
496 delta = ent->client->oldvelocity[2];
497 }
498 else
499 {
500 if (!ent->groundentity)
501 return;
502 delta = ent->velocity[2] - ent->client->oldvelocity[2];
503 }
504 delta = delta*delta * 0.0001;
505
506 // never take falling damage if completely underwater
507 if (ent->waterlevel == 3)
508 return;
509 if (ent->waterlevel == 2)
510 delta *= 0.25;
511 if (ent->waterlevel == 1)
512 delta *= 0.5;
513
514 if (delta < 1)
515 return;
516
517 if (delta < 15)
518 {
519 ent->s.event = EV_FOOTSTEP;
520 return;
521 }
522
523 ent->client->fall_value = delta*0.5;
524 if (ent->client->fall_value > 40)
525 ent->client->fall_value = 40;
526 ent->client->fall_time = level.time + FALL_TIME;
527
528 if (delta > 30)
529 {
530 if (ent->health > 0)
531 {
532 if (delta >= 55)
533 ent->s.event = EV_FALLFAR;
534 else
535 ent->s.event = EV_FALL;
536 }
537 ent->pain_debounce_time = level.time; // no normal pain sound
538 damage = (delta-30)/2;
539 if (damage < 1)
540 damage = 1;
541 VectorSet (dir, 0, 0, 1);
542
543 if (!deathmatch->value || !((int)dmflags->value & DF_NO_FALLING) )
544 T_Damage (ent, world, world, dir, ent->s.origin, vec3_origin, damage, 0, 0, MOD_FALLING);
545 }
546 else
547 {
548 ent->s.event = EV_FALLSHORT;
549 return;
550 }
551 }
552
553
554
555 /*
556 =============
557 P_WorldEffects
558 =============
559 */
P_WorldEffects(void)560 void P_WorldEffects (void)
561 {
562 qboolean breather;
563 qboolean envirosuit;
564 int waterlevel, old_waterlevel;
565
566 if (current_player->movetype == MOVETYPE_NOCLIP)
567 {
568 current_player->air_finished = level.time + 12; // don't need air
569 return;
570 }
571
572 waterlevel = current_player->waterlevel;
573 old_waterlevel = current_client->old_waterlevel;
574 current_client->old_waterlevel = waterlevel;
575
576 breather = current_client->breather_framenum > level.framenum;
577 envirosuit = current_client->enviro_framenum > level.framenum;
578
579 //
580 // if just entered a water volume, play a sound
581 //
582 if (!old_waterlevel && waterlevel)
583 {
584 PlayerNoise(current_player, current_player->s.origin, PNOISE_SELF);
585 if (current_player->watertype & CONTENTS_LAVA)
586 gi.sound (current_player, CHAN_BODY, gi.soundindex("player/lava_in.wav"), 1, ATTN_NORM, 0);
587 else if (current_player->watertype & CONTENTS_SLIME)
588 gi.sound (current_player, CHAN_BODY, gi.soundindex("player/watr_in.wav"), 1, ATTN_NORM, 0);
589 else if (current_player->watertype & CONTENTS_WATER)
590 gi.sound (current_player, CHAN_BODY, gi.soundindex("player/watr_in.wav"), 1, ATTN_NORM, 0);
591 current_player->flags |= FL_INWATER;
592
593 // clear damage_debounce, so the pain sound will play immediately
594 current_player->damage_debounce_time = level.time - 1;
595 }
596
597 //
598 // if just completely exited a water volume, play a sound
599 //
600 if (old_waterlevel && ! waterlevel)
601 {
602 PlayerNoise(current_player, current_player->s.origin, PNOISE_SELF);
603 gi.sound (current_player, CHAN_BODY, gi.soundindex("player/watr_out.wav"), 1, ATTN_NORM, 0);
604 current_player->flags &= ~FL_INWATER;
605 }
606
607 //
608 // check for head just going under water
609 //
610 if (old_waterlevel != 3 && waterlevel == 3)
611 {
612 gi.sound (current_player, CHAN_BODY, gi.soundindex("player/watr_un.wav"), 1, ATTN_NORM, 0);
613 }
614
615 //
616 // check for head just coming out of water
617 //
618 if (old_waterlevel == 3 && waterlevel != 3)
619 {
620 if (current_player->air_finished < level.time)
621 { // gasp for air
622 gi.sound (current_player, CHAN_VOICE, gi.soundindex("player/gasp1.wav"), 1, ATTN_NORM, 0);
623 PlayerNoise(current_player, current_player->s.origin, PNOISE_SELF);
624 }
625 else if (current_player->air_finished < level.time + 11)
626 { // just break surface
627 gi.sound (current_player, CHAN_VOICE, gi.soundindex("player/gasp2.wav"), 1, ATTN_NORM, 0);
628 }
629 }
630
631 //
632 // check for drowning
633 //
634 if (waterlevel == 3)
635 {
636 // breather or envirosuit give air
637 if (breather || envirosuit)
638 {
639 current_player->air_finished = level.time + 10;
640
641 if (((int)(current_client->breather_framenum - level.framenum) % 25) == 0)
642 {
643 if (!current_client->breather_sound)
644 gi.sound (current_player, CHAN_AUTO, gi.soundindex("player/u_breath1.wav"), 1, ATTN_NORM, 0);
645 else
646 gi.sound (current_player, CHAN_AUTO, gi.soundindex("player/u_breath2.wav"), 1, ATTN_NORM, 0);
647 current_client->breather_sound ^= 1;
648 PlayerNoise(current_player, current_player->s.origin, PNOISE_SELF);
649 //FIXME: release a bubble?
650 }
651 }
652
653 // if out of air, start drowning
654 if (current_player->air_finished < level.time)
655 { // drown!
656 if (current_player->client->next_drown_time < level.time
657 && current_player->health > 0)
658 {
659 current_player->client->next_drown_time = level.time + 1;
660
661 // take more damage the longer underwater
662 current_player->dmg += 2;
663 if (current_player->dmg > 15)
664 current_player->dmg = 15;
665
666 // play a gurp sound instead of a normal pain sound
667 if (current_player->health <= current_player->dmg)
668 gi.sound (current_player, CHAN_VOICE, gi.soundindex("player/drown1.wav"), 1, ATTN_NORM, 0);
669 else if (rand()&1)
670 gi.sound (current_player, CHAN_VOICE, gi.soundindex("*gurp1.wav"), 1, ATTN_NORM, 0);
671 else
672 gi.sound (current_player, CHAN_VOICE, gi.soundindex("*gurp2.wav"), 1, ATTN_NORM, 0);
673
674 current_player->pain_debounce_time = level.time;
675
676 T_Damage (current_player, world, world, vec3_origin, current_player->s.origin, vec3_origin, current_player->dmg, 0, DAMAGE_NO_ARMOR, MOD_WATER);
677 }
678 }
679 }
680 else
681 {
682 current_player->air_finished = level.time + 12;
683 current_player->dmg = 2;
684 }
685
686 //
687 // check for sizzle damage
688 //
689 if (waterlevel && (current_player->watertype&(CONTENTS_LAVA|CONTENTS_SLIME)) )
690 {
691 if (current_player->watertype & CONTENTS_LAVA)
692 {
693 if (current_player->health > 0
694 && current_player->pain_debounce_time <= level.time
695 && current_client->invincible_framenum < level.framenum)
696 {
697 if (rand()&1)
698 gi.sound (current_player, CHAN_VOICE, gi.soundindex("player/burn1.wav"), 1, ATTN_NORM, 0);
699 else
700 gi.sound (current_player, CHAN_VOICE, gi.soundindex("player/burn2.wav"), 1, ATTN_NORM, 0);
701 current_player->pain_debounce_time = level.time + 1;
702 }
703
704 if (envirosuit) // take 1/3 damage with envirosuit
705 T_Damage (current_player, world, world, vec3_origin, current_player->s.origin, vec3_origin, 1*waterlevel, 0, 0, MOD_LAVA);
706 else
707 T_Damage (current_player, world, world, vec3_origin, current_player->s.origin, vec3_origin, 3*waterlevel, 0, 0, MOD_LAVA);
708 }
709
710 if (current_player->watertype & CONTENTS_SLIME)
711 {
712 if (!envirosuit)
713 { // no damage from slime with envirosuit
714 T_Damage (current_player, world, world, vec3_origin, current_player->s.origin, vec3_origin, 1*waterlevel, 0, 0, MOD_SLIME);
715 }
716 }
717 }
718 }
719
720
721 /*
722 ===============
723 G_SetClientEffects
724 ===============
725 */
G_SetClientEffects(edict_t * ent)726 void G_SetClientEffects (edict_t *ent)
727 {
728 int pa_type;
729 int remaining;
730
731 ent->s.effects = 0;
732 ent->s.renderfx = 0;
733
734 if (ent->health <= 0 || level.intermissiontime)
735 return;
736
737 if (ent->powerarmor_time > level.time)
738 {
739 pa_type = PowerArmorType (ent);
740 if (pa_type == POWER_ARMOR_SCREEN)
741 {
742 ent->s.effects |= EF_POWERSCREEN;
743 }
744 else if (pa_type == POWER_ARMOR_SHIELD)
745 {
746 ent->s.effects |= EF_COLOR_SHELL;
747 ent->s.renderfx |= RF_SHELL_GREEN;
748 }
749 }
750
751 if (ent->client->quad_framenum > level.framenum)
752 {
753 remaining = ent->client->quad_framenum - level.framenum;
754 if (remaining > 30 || (remaining & 4) )
755 ent->s.effects |= EF_QUAD;
756 }
757
758 if (ent->client->invincible_framenum > level.framenum)
759 {
760 remaining = ent->client->invincible_framenum - level.framenum;
761 if (remaining > 30 || (remaining & 4) )
762 ent->s.effects |= EF_PENT;
763 }
764
765 // show cheaters!!!
766 if (ent->flags & FL_GODMODE)
767 {
768 ent->s.effects |= EF_COLOR_SHELL;
769 ent->s.renderfx |= (RF_SHELL_RED|RF_SHELL_GREEN|RF_SHELL_BLUE);
770 }
771 }
772
773
774 /*
775 ===============
776 G_SetClientEvent
777 ===============
778 */
G_SetClientEvent(edict_t * ent)779 void G_SetClientEvent (edict_t *ent)
780 {
781 if (ent->s.event)
782 return;
783
784 if ( ent->groundentity && xyspeed > 225)
785 {
786 if ( (int)(current_client->bobtime+bobmove) != bobcycle )
787 ent->s.event = EV_FOOTSTEP;
788 }
789 }
790
791 /*
792 ===============
793 G_SetClientSound
794 ===============
795 */
G_SetClientSound(edict_t * ent)796 void G_SetClientSound (edict_t *ent)
797 {
798 char *weap;
799
800 if (ent->client->pers.game_helpchanged != game.helpchanged)
801 {
802 ent->client->pers.game_helpchanged = game.helpchanged;
803 ent->client->pers.helpchanged = 1;
804 }
805
806 // help beep (no more than three times)
807 if (ent->client->pers.helpchanged && ent->client->pers.helpchanged <= 3 && !(level.framenum&63) )
808 {
809 ent->client->pers.helpchanged++;
810 gi.sound (ent, CHAN_VOICE, gi.soundindex ("misc/pc_up.wav"), 1, ATTN_STATIC, 0);
811 }
812
813
814 if (ent->client->pers.weapon)
815 weap = ent->client->pers.weapon->classname;
816 else
817 weap = "";
818
819 if (ent->waterlevel && (ent->watertype&(CONTENTS_LAVA|CONTENTS_SLIME)) )
820 ent->s.sound = snd_fry;
821 else if (strcmp(weap, "weapon_railgun") == 0)
822 ent->s.sound = gi.soundindex("weapons/rg_hum.wav");
823 else if (strcmp(weap, "weapon_bfg") == 0)
824 ent->s.sound = gi.soundindex("weapons/bfg_hum.wav");
825 else if (ent->client->weapon_sound)
826 ent->s.sound = ent->client->weapon_sound;
827 else
828 ent->s.sound = 0;
829 }
830
831 /*
832 ===============
833 G_SetClientFrame
834 ===============
835 */
G_SetClientFrame(edict_t * ent)836 void G_SetClientFrame (edict_t *ent)
837 {
838 gclient_t *client;
839 qboolean duck, run;
840
841 if (ent->s.modelindex != 255)
842 return; // not in the player model
843
844 client = ent->client;
845
846 if (client->ps.pmove.pm_flags & PMF_DUCKED)
847 duck = true;
848 else
849 duck = false;
850 if (xyspeed)
851 run = true;
852 else
853 run = false;
854
855 // check for stand/duck and stop/go transitions
856 if (duck != client->anim_duck && client->anim_priority < ANIM_DEATH)
857 goto newanim;
858 if (run != client->anim_run && client->anim_priority == ANIM_BASIC)
859 goto newanim;
860 if (!ent->groundentity && client->anim_priority <= ANIM_WAVE)
861 goto newanim;
862
863 if(client->anim_priority == ANIM_REVERSE)
864 {
865 if(ent->s.frame > client->anim_end)
866 {
867 ent->s.frame--;
868 return;
869 }
870 }
871 else if (ent->s.frame < client->anim_end)
872 { // continue an animation
873 ent->s.frame++;
874 return;
875 }
876
877 if (client->anim_priority == ANIM_DEATH)
878 return; // stay there
879 if (client->anim_priority == ANIM_JUMP)
880 {
881 if (!ent->groundentity)
882 return; // stay there
883 ent->client->anim_priority = ANIM_WAVE;
884 ent->s.frame = FRAME_jump3;
885 ent->client->anim_end = FRAME_jump6;
886 return;
887 }
888
889 newanim:
890 // return to either a running or standing frame
891 client->anim_priority = ANIM_BASIC;
892 client->anim_duck = duck;
893 client->anim_run = run;
894
895 if (!ent->groundentity)
896 {
897 client->anim_priority = ANIM_JUMP;
898 if (ent->s.frame != FRAME_jump2)
899 ent->s.frame = FRAME_jump1;
900 client->anim_end = FRAME_jump2;
901 }
902 else if (run)
903 { // running
904 if (duck)
905 {
906 ent->s.frame = FRAME_crwalk1;
907 client->anim_end = FRAME_crwalk6;
908 }
909 else
910 {
911 ent->s.frame = FRAME_run1;
912 client->anim_end = FRAME_run6;
913 }
914 }
915 else
916 { // standing
917 if (duck)
918 {
919 ent->s.frame = FRAME_crstnd01;
920 client->anim_end = FRAME_crstnd19;
921 }
922 else
923 {
924 ent->s.frame = FRAME_stand01;
925 client->anim_end = FRAME_stand40;
926 }
927 }
928 }
929
930
931 /*
932 =================
933 ClientEndServerFrame
934
935 Called for each player at the end of the server frame
936 and right after spawning
937 =================
938 */
ClientEndServerFrame(edict_t * ent)939 void ClientEndServerFrame (edict_t *ent)
940 {
941 float bobtime;
942 int i;
943
944 current_player = ent;
945 current_client = ent->client;
946
947 //
948 // If the origin or velocity have changed since ClientThink(),
949 // update the pmove values. This will happen when the client
950 // is pushed by a bmodel or kicked by an explosion.
951 //
952 // If it wasn't updated here, the view position would lag a frame
953 // behind the body position when pushed -- "sinking into plats"
954 //
955 for (i=0 ; i<3 ; i++)
956 {
957 current_client->ps.pmove.origin[i] = ent->s.origin[i]*8.0;
958 current_client->ps.pmove.velocity[i] = ent->velocity[i]*8.0;
959 }
960
961 //
962 // If the end of unit layout is displayed, don't give
963 // the player any normal movement attributes
964 //
965 if (level.intermissiontime)
966 {
967 // FIXME: add view drifting here?
968 current_client->ps.blend[3] = 0;
969 current_client->ps.fov = 90;
970 G_SetStats (ent);
971 return;
972 }
973
974 AngleVectors (ent->client->v_angle, forward, right, up);
975
976 // burn from lava, etc
977 P_WorldEffects ();
978
979 //
980 // set model angles from view angles so other things in
981 // the world can tell which direction you are looking
982 //
983 if (ent->client->v_angle[PITCH] > 180)
984 ent->s.angles[PITCH] = (-360 + ent->client->v_angle[PITCH])/3;
985 else
986 ent->s.angles[PITCH] = ent->client->v_angle[PITCH]/3;
987 ent->s.angles[YAW] = ent->client->v_angle[YAW];
988 ent->s.angles[ROLL] = 0;
989 ent->s.angles[ROLL] = SV_CalcRoll (ent->s.angles, ent->velocity)*4;
990
991 //
992 // calculate speed and cycle to be used for
993 // all cyclic walking effects
994 //
995 xyspeed = sqrt(ent->velocity[0]*ent->velocity[0] + ent->velocity[1]*ent->velocity[1]);
996
997 if (xyspeed < 5)
998 {
999 bobmove = 0;
1000 current_client->bobtime = 0; // start at beginning of cycle again
1001 }
1002 else if (ent->groundentity)
1003 { // so bobbing only cycles when on ground
1004 if (xyspeed > 210)
1005 bobmove = 0.25;
1006 else if (xyspeed > 100)
1007 bobmove = 0.125;
1008 else
1009 bobmove = 0.0625;
1010 }
1011
1012 bobtime = (current_client->bobtime += bobmove);
1013
1014 if (current_client->ps.pmove.pm_flags & PMF_DUCKED)
1015 bobtime *= 4;
1016
1017 bobcycle = (int)bobtime;
1018 bobfracsin = fabs(sin(bobtime*M_PI));
1019
1020 // detect hitting the floor
1021 P_FallingDamage (ent);
1022
1023 // apply all the damage taken this frame
1024 P_DamageFeedback (ent);
1025
1026 // determine the view offsets
1027 SV_CalcViewOffset (ent);
1028
1029 // determine the gun offsets
1030 SV_CalcGunOffset (ent);
1031
1032 // determine the full screen color blend
1033 // must be after viewoffset, so eye contents can be
1034 // accurately determined
1035 // FIXME: with client prediction, the contents
1036 // should be determined by the client
1037 SV_CalcBlend (ent);
1038
1039 // chase cam stuff
1040 if (ent->client->resp.spectator)
1041 G_SetSpectatorStats(ent);
1042 else
1043 G_SetStats (ent);
1044 G_CheckChaseStats(ent);
1045
1046 G_SetClientEvent (ent);
1047
1048 G_SetClientEffects (ent);
1049
1050 G_SetClientSound (ent);
1051
1052 G_SetClientFrame (ent);
1053
1054 VectorCopy (ent->velocity, ent->client->oldvelocity);
1055 VectorCopy (ent->client->ps.viewangles, ent->client->oldviewangles);
1056
1057 // clear weapon kicks
1058 VectorClear (ent->client->kick_origin);
1059 VectorClear (ent->client->kick_angles);
1060
1061 // if the scoreboard is up, update it
1062 if (ent->client->showscores && !(level.framenum & 31) )
1063 {
1064 DeathmatchScoreboardMessage (ent, ent->enemy);
1065 gi.unicast (ent, false);
1066 }
1067 }
1068
1069