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; // allways 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 allways 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 //ZOID
526 // never take damage if just release grapple or on grapple
527 if (level.time - ent->client->ctf_grapplereleasetime <= FRAMETIME * 2 ||
528 (ent->client->ctf_grapple &&
529 ent->client->ctf_grapplestate > CTF_GRAPPLE_STATE_FLY))
530 return;
531 //ZOID
532
533 // never take falling damage if completely underwater
534 if (ent->waterlevel == 3)
535 return;
536 if (ent->waterlevel == 2)
537 delta *= 0.25;
538 if (ent->waterlevel == 1)
539 delta *= 0.5;
540
541 if (delta < 1)
542 return;
543
544 if (delta < 15)
545 {
546 ent->s.event = EV_FOOTSTEP;
547 return;
548 }
549
550 ent->client->fall_value = delta*0.5;
551 if (ent->client->fall_value > 40)
552 ent->client->fall_value = 40;
553 ent->client->fall_time = level.time + FALL_TIME;
554
555 if (delta > 30)
556 {
557 if (ent->health > 0)
558 {
559 if (delta >= 55)
560 ent->s.event = EV_FALLFAR;
561 else
562 ent->s.event = EV_FALL;
563 }
564 ent->pain_debounce_time = level.time; // no normal pain sound
565 damage = (delta-30)/2;
566 if (damage < 1)
567 damage = 1;
568 VectorSet (dir, 0, 0, 1);
569
570 if (!deathmatch->value || !((int)dmflags->value & DF_NO_FALLING) )
571 T_Damage (ent, world, world, dir, ent->s.origin, vec3_origin, damage, 0, 0, MOD_FALLING);
572 }
573 else
574 {
575 ent->s.event = EV_FALLSHORT;
576 return;
577 }
578 }
579
580
581
582 /*
583 =============
584 P_WorldEffects
585 =============
586 */
P_WorldEffects(void)587 void P_WorldEffects (void)
588 {
589 qboolean breather;
590 qboolean envirosuit;
591 int waterlevel, old_waterlevel;
592
593 if (current_player->movetype == MOVETYPE_NOCLIP)
594 {
595 current_player->air_finished = level.time + 12; // don't need air
596 return;
597 }
598
599 waterlevel = current_player->waterlevel;
600 old_waterlevel = current_client->old_waterlevel;
601 current_client->old_waterlevel = waterlevel;
602
603 breather = current_client->breather_framenum > level.framenum;
604 envirosuit = current_client->enviro_framenum > level.framenum;
605
606 //
607 // if just entered a water volume, play a sound
608 //
609 if (!old_waterlevel && waterlevel)
610 {
611 PlayerNoise(current_player, current_player->s.origin, PNOISE_SELF);
612 if (current_player->watertype & CONTENTS_LAVA)
613 gi.sound (current_player, CHAN_BODY, gi.soundindex("player/lava_in.wav"), 1, ATTN_NORM, 0);
614 else if (current_player->watertype & CONTENTS_SLIME)
615 gi.sound (current_player, CHAN_BODY, gi.soundindex("player/watr_in.wav"), 1, ATTN_NORM, 0);
616 else if (current_player->watertype & CONTENTS_WATER)
617 gi.sound (current_player, CHAN_BODY, gi.soundindex("player/watr_in.wav"), 1, ATTN_NORM, 0);
618 current_player->flags |= FL_INWATER;
619
620 // clear damage_debounce, so the pain sound will play immediately
621 current_player->damage_debounce_time = level.time - 1;
622 }
623
624 //
625 // if just completely exited a water volume, play a sound
626 //
627 if (old_waterlevel && ! waterlevel)
628 {
629 PlayerNoise(current_player, current_player->s.origin, PNOISE_SELF);
630 gi.sound (current_player, CHAN_BODY, gi.soundindex("player/watr_out.wav"), 1, ATTN_NORM, 0);
631 current_player->flags &= ~FL_INWATER;
632 }
633
634 //
635 // check for head just going under water
636 //
637 if (old_waterlevel != 3 && waterlevel == 3)
638 {
639 gi.sound (current_player, CHAN_BODY, gi.soundindex("player/watr_un.wav"), 1, ATTN_NORM, 0);
640 }
641
642 //
643 // check for head just coming out of water
644 //
645 if (old_waterlevel == 3 && waterlevel != 3)
646 {
647 if (current_player->air_finished < level.time)
648 { // gasp for air
649 gi.sound (current_player, CHAN_VOICE, gi.soundindex("player/gasp1.wav"), 1, ATTN_NORM, 0);
650 PlayerNoise(current_player, current_player->s.origin, PNOISE_SELF);
651 }
652 else if (current_player->air_finished < level.time + 11)
653 { // just break surface
654 gi.sound (current_player, CHAN_VOICE, gi.soundindex("player/gasp2.wav"), 1, ATTN_NORM, 0);
655 }
656 }
657
658 //
659 // check for drowning
660 //
661 if (waterlevel == 3)
662 {
663 // breather or envirosuit give air
664 if (breather || envirosuit)
665 {
666 current_player->air_finished = level.time + 10;
667
668 if (((int)(current_client->breather_framenum - level.framenum) % 25) == 0)
669 {
670 if (!current_client->breather_sound)
671 gi.sound (current_player, CHAN_AUTO, gi.soundindex("player/u_breath1.wav"), 1, ATTN_NORM, 0);
672 else
673 gi.sound (current_player, CHAN_AUTO, gi.soundindex("player/u_breath2.wav"), 1, ATTN_NORM, 0);
674 current_client->breather_sound ^= 1;
675 PlayerNoise(current_player, current_player->s.origin, PNOISE_SELF);
676 //FIXME: release a bubble?
677 }
678 }
679
680 // if out of air, start drowning
681 if (current_player->air_finished < level.time)
682 { // drown!
683 if (current_player->client->next_drown_time < level.time
684 && current_player->health > 0)
685 {
686 current_player->client->next_drown_time = level.time + 1;
687
688 // take more damage the longer underwater
689 current_player->dmg += 2;
690 if (current_player->dmg > 15)
691 current_player->dmg = 15;
692
693 // play a gurp sound instead of a normal pain sound
694 if (current_player->health <= current_player->dmg)
695 gi.sound (current_player, CHAN_VOICE, gi.soundindex("player/drown1.wav"), 1, ATTN_NORM, 0);
696 else if (rand()&1)
697 gi.sound (current_player, CHAN_VOICE, gi.soundindex("*gurp1.wav"), 1, ATTN_NORM, 0);
698 else
699 gi.sound (current_player, CHAN_VOICE, gi.soundindex("*gurp2.wav"), 1, ATTN_NORM, 0);
700
701 current_player->pain_debounce_time = level.time;
702
703 T_Damage (current_player, world, world, vec3_origin, current_player->s.origin, vec3_origin, current_player->dmg, 0, DAMAGE_NO_ARMOR, MOD_WATER);
704 }
705 }
706 }
707 else
708 {
709 current_player->air_finished = level.time + 12;
710 current_player->dmg = 2;
711 }
712
713 //
714 // check for sizzle damage
715 //
716 if (waterlevel && (current_player->watertype&(CONTENTS_LAVA|CONTENTS_SLIME)) )
717 {
718 if (current_player->watertype & CONTENTS_LAVA)
719 {
720 if (current_player->health > 0
721 && current_player->pain_debounce_time <= level.time
722 && current_client->invincible_framenum < level.framenum)
723 {
724 if (rand()&1)
725 gi.sound (current_player, CHAN_VOICE, gi.soundindex("player/burn1.wav"), 1, ATTN_NORM, 0);
726 else
727 gi.sound (current_player, CHAN_VOICE, gi.soundindex("player/burn2.wav"), 1, ATTN_NORM, 0);
728 current_player->pain_debounce_time = level.time + 1;
729 }
730
731 if (envirosuit) // take 1/3 damage with envirosuit
732 T_Damage (current_player, world, world, vec3_origin, current_player->s.origin, vec3_origin, 1*waterlevel, 0, 0, MOD_LAVA);
733 else
734 T_Damage (current_player, world, world, vec3_origin, current_player->s.origin, vec3_origin, 3*waterlevel, 0, 0, MOD_LAVA);
735 }
736
737 if (current_player->watertype & CONTENTS_SLIME)
738 {
739 if (!envirosuit)
740 { // no damage from slime with envirosuit
741 T_Damage (current_player, world, world, vec3_origin, current_player->s.origin, vec3_origin, 1*waterlevel, 0, 0, MOD_SLIME);
742 }
743 }
744 }
745 }
746
747
748 /*
749 ===============
750 G_SetClientEffects
751 ===============
752 */
G_SetClientEffects(edict_t * ent)753 void G_SetClientEffects (edict_t *ent)
754 {
755 int pa_type;
756 int remaining;
757
758 ent->s.effects = 0;
759 ent->s.renderfx = 0;
760
761 if (ent->health <= 0 || level.intermissiontime)
762 return;
763
764 if (ent->powerarmor_time > level.time)
765 {
766 pa_type = PowerArmorType (ent);
767 if (pa_type == POWER_ARMOR_SCREEN)
768 {
769 ent->s.effects |= EF_POWERSCREEN;
770 }
771 else if (pa_type == POWER_ARMOR_SHIELD)
772 {
773 ent->s.effects |= EF_COLOR_SHELL;
774 ent->s.renderfx |= RF_SHELL_GREEN;
775 }
776 }
777
778 //ZOID
779 CTFEffects(ent);
780 //ZOID
781
782 if (ent->client->quad_framenum > level.framenum)
783 {
784 remaining = ent->client->quad_framenum - level.framenum;
785 if (remaining > 30 || (remaining & 4) )
786 // ent->s.effects |= EF_QUAD;
787 CTFSetPowerUpEffect(ent, EF_QUAD);
788 }
789
790 if (ent->client->invincible_framenum > level.framenum)
791 {
792 remaining = ent->client->invincible_framenum - level.framenum;
793 if (remaining > 30 || (remaining & 4) )
794 // ent->s.effects |= EF_PENT;
795 CTFSetPowerUpEffect(ent, EF_PENT);
796 }
797
798 // show cheaters!!!
799 if (ent->flags & FL_GODMODE)
800 {
801 ent->s.effects |= EF_COLOR_SHELL;
802 ent->s.renderfx |= (RF_SHELL_RED|RF_SHELL_GREEN|RF_SHELL_BLUE);
803 }
804 }
805
806
807 /*
808 ===============
809 G_SetClientEvent
810 ===============
811 */
G_SetClientEvent(edict_t * ent)812 void G_SetClientEvent (edict_t *ent)
813 {
814 if (ent->s.event)
815 return;
816
817 if ( ent->groundentity && xyspeed > 225)
818 {
819 if ( (int)(current_client->bobtime+bobmove) != bobcycle )
820 ent->s.event = EV_FOOTSTEP;
821 }
822 }
823
824 /*
825 ===============
826 G_SetClientSound
827 ===============
828 */
G_SetClientSound(edict_t * ent)829 void G_SetClientSound (edict_t *ent)
830 {
831 char *weap;
832
833 if (ent->client->resp.game_helpchanged != game.helpchanged)
834 {
835 ent->client->resp.game_helpchanged = game.helpchanged;
836 ent->client->resp.helpchanged = 1;
837 }
838
839 // help beep (no more than three times)
840 if (ent->client->resp.helpchanged && ent->client->resp.helpchanged <= 3 && !(level.framenum&63) )
841 {
842 ent->client->resp.helpchanged++;
843 gi.sound (ent, CHAN_VOICE, gi.soundindex ("misc/pc_up.wav"), 1, ATTN_STATIC, 0);
844 }
845
846
847 if (ent->client->pers.weapon)
848 weap = ent->client->pers.weapon->classname;
849 else
850 weap = "";
851
852 if (ent->waterlevel && (ent->watertype&(CONTENTS_LAVA|CONTENTS_SLIME)) )
853 ent->s.sound = snd_fry;
854 else if (strcmp(weap, "weapon_railgun") == 0)
855 ent->s.sound = gi.soundindex("weapons/rg_hum.wav");
856 else if (strcmp(weap, "weapon_bfg") == 0)
857 ent->s.sound = gi.soundindex("weapons/bfg_hum.wav");
858 else if (ent->client->weapon_sound)
859 ent->s.sound = ent->client->weapon_sound;
860 else
861 ent->s.sound = 0;
862 }
863
864 /*
865 ===============
866 G_SetClientFrame
867 ===============
868 */
G_SetClientFrame(edict_t * ent)869 void G_SetClientFrame (edict_t *ent)
870 {
871 gclient_t *client;
872 qboolean duck, run;
873
874 if (ent->s.modelindex != 255)
875 return; // not in the player model
876
877 client = ent->client;
878
879 if (client->ps.pmove.pm_flags & PMF_DUCKED)
880 duck = true;
881 else
882 duck = false;
883 if (xyspeed)
884 run = true;
885 else
886 run = false;
887
888 // check for stand/duck and stop/go transitions
889 if (duck != client->anim_duck && client->anim_priority < ANIM_DEATH)
890 goto newanim;
891 if (run != client->anim_run && client->anim_priority == ANIM_BASIC)
892 goto newanim;
893 if (!ent->groundentity && client->anim_priority <= ANIM_WAVE)
894 goto newanim;
895
896 if(client->anim_priority == ANIM_REVERSE)
897 {
898 if(ent->s.frame > client->anim_end)
899 {
900 ent->s.frame--;
901 return;
902 }
903 }
904 else if (ent->s.frame < client->anim_end)
905 { // continue an animation
906 ent->s.frame++;
907 return;
908 }
909
910 if (client->anim_priority == ANIM_DEATH)
911 return; // stay there
912 if (client->anim_priority == ANIM_JUMP)
913 {
914 if (!ent->groundentity)
915 return; // stay there
916 ent->client->anim_priority = ANIM_WAVE;
917 ent->s.frame = FRAME_jump3;
918 ent->client->anim_end = FRAME_jump6;
919 return;
920 }
921
922 newanim:
923 // return to either a running or standing frame
924 client->anim_priority = ANIM_BASIC;
925 client->anim_duck = duck;
926 client->anim_run = run;
927
928 if (!ent->groundentity)
929 {
930 //ZOID: if on grapple, don't go into jump frame, go into standing
931 //frame
932 if (client->ctf_grapple) {
933 ent->s.frame = FRAME_stand01;
934 client->anim_end = FRAME_stand40;
935 } else {
936 //ZOID
937 client->anim_priority = ANIM_JUMP;
938 if (ent->s.frame != FRAME_jump2)
939 ent->s.frame = FRAME_jump1;
940 client->anim_end = FRAME_jump2;
941 }
942 }
943 else if (run)
944 { // running
945 if (duck)
946 {
947 ent->s.frame = FRAME_crwalk1;
948 client->anim_end = FRAME_crwalk6;
949 }
950 else
951 {
952 ent->s.frame = FRAME_run1;
953 client->anim_end = FRAME_run6;
954 }
955 }
956 else
957 { // standing
958 if (duck)
959 {
960 ent->s.frame = FRAME_crstnd01;
961 client->anim_end = FRAME_crstnd19;
962 }
963 else
964 {
965 ent->s.frame = FRAME_stand01;
966 client->anim_end = FRAME_stand40;
967 }
968 }
969 }
970
971
972 /*
973 =================
974 ClientEndServerFrame
975
976 Called for each player at the end of the server frame
977 and right after spawning
978 =================
979 */
ClientEndServerFrame(edict_t * ent)980 void ClientEndServerFrame (edict_t *ent)
981 {
982 float bobtime;
983 int i;
984
985 current_player = ent;
986 current_client = ent->client;
987
988 //
989 // If the origin or velocity have changed since ClientThink(),
990 // update the pmove values. This will happen when the client
991 // is pushed by a bmodel or kicked by an explosion.
992 //
993 // If it wasn't updated here, the view position would lag a frame
994 // behind the body position when pushed -- "sinking into plats"
995 //
996 for (i=0 ; i<3 ; i++)
997 {
998 current_client->ps.pmove.origin[i] = ent->s.origin[i]*8.0;
999 current_client->ps.pmove.velocity[i] = ent->velocity[i]*8.0;
1000 }
1001
1002 //
1003 // If the end of unit layout is displayed, don't give
1004 // the player any normal movement attributes
1005 //
1006 if (level.intermissiontime)
1007 {
1008 // FIXME: add view drifting here?
1009 current_client->ps.blend[3] = 0;
1010 current_client->ps.fov = 90;
1011 G_SetStats (ent);
1012 return;
1013 }
1014
1015 AngleVectors (ent->client->v_angle, forward, right, up);
1016
1017 // burn from lava, etc
1018 P_WorldEffects ();
1019
1020 //
1021 // set model angles from view angles so other things in
1022 // the world can tell which direction you are looking
1023 //
1024 if (ent->client->v_angle[PITCH] > 180)
1025 ent->s.angles[PITCH] = (-360 + ent->client->v_angle[PITCH])/3;
1026 else
1027 ent->s.angles[PITCH] = ent->client->v_angle[PITCH]/3;
1028 ent->s.angles[YAW] = ent->client->v_angle[YAW];
1029 ent->s.angles[ROLL] = 0;
1030 ent->s.angles[ROLL] = SV_CalcRoll (ent->s.angles, ent->velocity)*4;
1031
1032 //
1033 // calculate speed and cycle to be used for
1034 // all cyclic walking effects
1035 //
1036 xyspeed = sqrt(ent->velocity[0]*ent->velocity[0] + ent->velocity[1]*ent->velocity[1]);
1037
1038 if (xyspeed < 5)
1039 {
1040 bobmove = 0;
1041 current_client->bobtime = 0; // start at beginning of cycle again
1042 }
1043 else if (ent->groundentity)
1044 { // so bobbing only cycles when on ground
1045 if (xyspeed > 210)
1046 bobmove = 0.25;
1047 else if (xyspeed > 100)
1048 bobmove = 0.125;
1049 else
1050 bobmove = 0.0625;
1051 }
1052
1053 bobtime = (current_client->bobtime += bobmove);
1054
1055 if (current_client->ps.pmove.pm_flags & PMF_DUCKED)
1056 bobtime *= 4;
1057
1058 bobcycle = (int)bobtime;
1059 bobfracsin = fabs(sin(bobtime*M_PI));
1060
1061 // detect hitting the floor
1062 P_FallingDamage (ent);
1063
1064 // apply all the damage taken this frame
1065 P_DamageFeedback (ent);
1066
1067 // determine the view offsets
1068 SV_CalcViewOffset (ent);
1069
1070 // determine the gun offsets
1071 SV_CalcGunOffset (ent);
1072
1073 // determine the full screen color blend
1074 // must be after viewoffset, so eye contents can be
1075 // accurately determined
1076 // FIXME: with client prediction, the contents
1077 // should be determined by the client
1078 SV_CalcBlend (ent);
1079
1080 //ZOID
1081 if (!ent->client->chase_target)
1082 //ZOID
1083 G_SetStats (ent);
1084
1085 //ZOID
1086 //update chasecam follower stats
1087 for (i = 1; i <= maxclients->value; i++) {
1088 edict_t *e = g_edicts + i;
1089 if (!e->inuse || e->client->chase_target != ent)
1090 continue;
1091 memcpy(e->client->ps.stats,
1092 ent->client->ps.stats,
1093 sizeof(ent->client->ps.stats));
1094 e->client->ps.stats[STAT_LAYOUTS] = 1;
1095 break;
1096 }
1097 //ZOID
1098
1099
1100 G_SetClientEvent (ent);
1101
1102 G_SetClientEffects (ent);
1103
1104 G_SetClientSound (ent);
1105
1106 G_SetClientFrame (ent);
1107
1108 VectorCopy (ent->velocity, ent->client->oldvelocity);
1109 VectorCopy (ent->client->ps.viewangles, ent->client->oldviewangles);
1110
1111 // clear weapon kicks
1112 VectorClear (ent->client->kick_origin);
1113 VectorClear (ent->client->kick_angles);
1114
1115 // if the scoreboard is up, update it
1116 if (ent->client->showscores && !(level.framenum & 31) )
1117 {
1118 //ZOID
1119 if (ent->client->menu) {
1120 PMenu_Do_Update(ent);
1121 ent->client->menudirty = false;
1122 ent->client->menutime = level.time;
1123 } else
1124 //ZOID
1125 DeathmatchScoreboardMessage (ent, ent->enemy);
1126 gi.unicast (ent, false);
1127 }
1128 }
1129
1130