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