p_view.c (26675B)
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 */ 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 */ 71 void P_DamageFeedback (edict_t *player) 72 { 73 gclient_t *client; 74 float side; 75 float realcount, count, kick; 76 vec3_t v; 77 int r, l; 78 static vec3_t power_color = {0.0, 1.0, 0.0}; 79 static vec3_t acolor = {1.0, 1.0, 1.0}; 80 static vec3_t bcolor = {1.0, 0.0, 0.0}; 81 82 client = player->client; 83 84 // flash the backgrounds behind the status numbers 85 client->ps.stats[STAT_FLASHES] = 0; 86 if (client->damage_blood) 87 client->ps.stats[STAT_FLASHES] |= 1; 88 if (client->damage_armor && !(player->flags & FL_GODMODE) && (client->invincible_framenum <= level.framenum)) 89 client->ps.stats[STAT_FLASHES] |= 2; 90 91 // total points of damage shot at the player this frame 92 count = (client->damage_blood + client->damage_armor + client->damage_parmor); 93 if (count == 0) 94 return; // didn't take any damage 95 96 // start a pain animation if still in the player model 97 if (client->anim_priority < ANIM_PAIN && player->s.modelindex == 255) 98 { 99 static int i; 100 101 client->anim_priority = ANIM_PAIN; 102 if (client->ps.pmove.pm_flags & PMF_DUCKED) 103 { 104 player->s.frame = FRAME_crpain1-1; 105 client->anim_end = FRAME_crpain4; 106 } 107 else 108 { 109 i = (i+1)%3; 110 switch (i) 111 { 112 case 0: 113 player->s.frame = FRAME_pain101-1; 114 client->anim_end = FRAME_pain104; 115 break; 116 case 1: 117 player->s.frame = FRAME_pain201-1; 118 client->anim_end = FRAME_pain204; 119 break; 120 case 2: 121 player->s.frame = FRAME_pain301-1; 122 client->anim_end = FRAME_pain304; 123 break; 124 } 125 } 126 } 127 128 realcount = count; 129 if (count < 10) 130 count = 10; // always make a visible effect 131 132 // play an apropriate pain sound 133 if ((level.time > player->pain_debounce_time) && !(player->flags & FL_GODMODE) && (client->invincible_framenum <= level.framenum)) 134 { 135 r = 1 + (rand()&1); 136 player->pain_debounce_time = level.time + 0.7; 137 if (player->health < 25) 138 l = 25; 139 else if (player->health < 50) 140 l = 50; 141 else if (player->health < 75) 142 l = 75; 143 else 144 l = 100; 145 gi.sound (player, CHAN_VOICE, gi.soundindex(va("*pain%i_%i.wav", l, r)), 1, ATTN_NORM, 0); 146 } 147 148 // the total alpha of the blend is always proportional to count 149 if (client->damage_alpha < 0) 150 client->damage_alpha = 0; 151 client->damage_alpha += count*0.01; 152 if (client->damage_alpha < 0.2) 153 client->damage_alpha = 0.2; 154 if (client->damage_alpha > 0.6) 155 client->damage_alpha = 0.6; // don't go too saturated 156 157 // the color of the blend will vary based on how much was absorbed 158 // by different armors 159 VectorClear (v); 160 if (client->damage_parmor) 161 VectorMA (v, (float)client->damage_parmor/realcount, power_color, v); 162 if (client->damage_armor) 163 VectorMA (v, (float)client->damage_armor/realcount, acolor, v); 164 if (client->damage_blood) 165 VectorMA (v, (float)client->damage_blood/realcount, bcolor, v); 166 VectorCopy (v, client->damage_blend); 167 168 169 // 170 // calculate view angle kicks 171 // 172 kick = abs(client->damage_knockback); 173 if (kick && player->health > 0) // kick of 0 means no view adjust at all 174 { 175 kick = kick * 100 / player->health; 176 177 if (kick < count*0.5) 178 kick = count*0.5; 179 if (kick > 50) 180 kick = 50; 181 182 VectorSubtract (client->damage_from, player->s.origin, v); 183 VectorNormalize (v); 184 185 side = DotProduct (v, right); 186 client->v_dmg_roll = kick*side*0.3; 187 188 side = -DotProduct (v, forward); 189 client->v_dmg_pitch = kick*side*0.3; 190 191 client->v_dmg_time = level.time + DAMAGE_TIME; 192 } 193 194 // 195 // clear totals 196 // 197 client->damage_blood = 0; 198 client->damage_armor = 0; 199 client->damage_parmor = 0; 200 client->damage_knockback = 0; 201 } 202 203 204 205 206 /* 207 =============== 208 SV_CalcViewOffset 209 210 Auto pitching on slopes? 211 212 fall from 128: 400 = 160000 213 fall from 256: 580 = 336400 214 fall from 384: 720 = 518400 215 fall from 512: 800 = 640000 216 fall from 640: 960 = 217 218 damage = deltavelocity*deltavelocity * 0.0001 219 220 =============== 221 */ 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 */ 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 */ 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 */ 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 */ 501 void P_FallingDamage (edict_t *ent) 502 { 503 float delta; 504 int damage; 505 vec3_t dir; 506 507 if (ent->s.modelindex != 255) 508 return; // not in the player model 509 510 if (ent->movetype == MOVETYPE_NOCLIP) 511 return; 512 513 if ((ent->client->oldvelocity[2] < 0) && (ent->velocity[2] > ent->client->oldvelocity[2]) && (!ent->groundentity)) 514 { 515 delta = ent->client->oldvelocity[2]; 516 } 517 else 518 { 519 if (!ent->groundentity) 520 return; 521 delta = ent->velocity[2] - ent->client->oldvelocity[2]; 522 } 523 delta = delta*delta * 0.0001; 524 525 // never take falling damage if completely underwater 526 if (ent->waterlevel == 3) 527 return; 528 if (ent->waterlevel == 2) 529 delta *= 0.25; 530 if (ent->waterlevel == 1) 531 delta *= 0.5; 532 533 if (delta < 1) 534 return; 535 536 if (delta < 15) 537 { 538 ent->s.event = EV_FOOTSTEP; 539 return; 540 } 541 542 ent->client->fall_value = delta*0.5; 543 if (ent->client->fall_value > 40) 544 ent->client->fall_value = 40; 545 ent->client->fall_time = level.time + FALL_TIME; 546 547 if (delta > 30) 548 { 549 if (ent->health > 0) 550 { 551 if (delta >= 55) 552 ent->s.event = EV_FALLFAR; 553 else 554 ent->s.event = EV_FALL; 555 } 556 ent->pain_debounce_time = level.time; // no normal pain sound 557 damage = (delta-30)/2; 558 if (damage < 1) 559 damage = 1; 560 VectorSet (dir, 0, 0, 1); 561 562 if (!deathmatch->value || !((int)dmflags->value & DF_NO_FALLING) ) 563 T_Damage (ent, world, world, dir, ent->s.origin, vec3_origin, damage, 0, 0, MOD_FALLING); 564 } 565 else 566 { 567 ent->s.event = EV_FALLSHORT; 568 return; 569 } 570 } 571 572 573 574 /* 575 ============= 576 P_WorldEffects 577 ============= 578 */ 579 void P_WorldEffects (void) 580 { 581 qboolean breather; 582 qboolean envirosuit; 583 int waterlevel, old_waterlevel; 584 585 if (current_player->movetype == MOVETYPE_NOCLIP) 586 { 587 current_player->air_finished = level.time + 12; // don't need air 588 return; 589 } 590 591 waterlevel = current_player->waterlevel; 592 old_waterlevel = current_client->old_waterlevel; 593 current_client->old_waterlevel = waterlevel; 594 595 breather = current_client->breather_framenum > level.framenum; 596 envirosuit = current_client->enviro_framenum > level.framenum; 597 598 // 599 // if just entered a water volume, play a sound 600 // 601 if (!old_waterlevel && waterlevel) 602 { 603 PlayerNoise(current_player, current_player->s.origin, PNOISE_SELF); 604 if (current_player->watertype & CONTENTS_LAVA) 605 gi.sound (current_player, CHAN_BODY, gi.soundindex("player/lava_in.wav"), 1, ATTN_NORM, 0); 606 else if (current_player->watertype & CONTENTS_SLIME) 607 gi.sound (current_player, CHAN_BODY, gi.soundindex("player/watr_in.wav"), 1, ATTN_NORM, 0); 608 else if (current_player->watertype & CONTENTS_WATER) 609 gi.sound (current_player, CHAN_BODY, gi.soundindex("player/watr_in.wav"), 1, ATTN_NORM, 0); 610 current_player->flags |= FL_INWATER; 611 612 // clear damage_debounce, so the pain sound will play immediately 613 current_player->damage_debounce_time = level.time - 1; 614 } 615 616 // 617 // if just completely exited a water volume, play a sound 618 // 619 if (old_waterlevel && ! waterlevel) 620 { 621 PlayerNoise(current_player, current_player->s.origin, PNOISE_SELF); 622 gi.sound (current_player, CHAN_BODY, gi.soundindex("player/watr_out.wav"), 1, ATTN_NORM, 0); 623 current_player->flags &= ~FL_INWATER; 624 } 625 626 // 627 // check for head just going under water 628 // 629 if (old_waterlevel != 3 && waterlevel == 3) 630 { 631 gi.sound (current_player, CHAN_BODY, gi.soundindex("player/watr_un.wav"), 1, ATTN_NORM, 0); 632 } 633 634 // 635 // check for head just coming out of water 636 // 637 if (old_waterlevel == 3 && waterlevel != 3) 638 { 639 if (current_player->air_finished < level.time) 640 { // gasp for air 641 gi.sound (current_player, CHAN_VOICE, gi.soundindex("player/gasp1.wav"), 1, ATTN_NORM, 0); 642 PlayerNoise(current_player, current_player->s.origin, PNOISE_SELF); 643 } 644 else if (current_player->air_finished < level.time + 11) 645 { // just break surface 646 gi.sound (current_player, CHAN_VOICE, gi.soundindex("player/gasp2.wav"), 1, ATTN_NORM, 0); 647 } 648 } 649 650 // 651 // check for drowning 652 // 653 if (waterlevel == 3) 654 { 655 // breather or envirosuit give air 656 if (breather || envirosuit) 657 { 658 current_player->air_finished = level.time + 10; 659 660 if (((int)(current_client->breather_framenum - level.framenum) % 25) == 0) 661 { 662 if (!current_client->breather_sound) 663 gi.sound (current_player, CHAN_AUTO, gi.soundindex("player/u_breath1.wav"), 1, ATTN_NORM, 0); 664 else 665 gi.sound (current_player, CHAN_AUTO, gi.soundindex("player/u_breath2.wav"), 1, ATTN_NORM, 0); 666 current_client->breather_sound ^= 1; 667 PlayerNoise(current_player, current_player->s.origin, PNOISE_SELF); 668 //FIXME: release a bubble? 669 } 670 } 671 672 // if out of air, start drowning 673 if (current_player->air_finished < level.time) 674 { // drown! 675 if (current_player->client->next_drown_time < level.time 676 && current_player->health > 0) 677 { 678 current_player->client->next_drown_time = level.time + 1; 679 680 // take more damage the longer underwater 681 current_player->dmg += 2; 682 if (current_player->dmg > 15) 683 current_player->dmg = 15; 684 685 // play a gurp sound instead of a normal pain sound 686 if (current_player->health <= current_player->dmg) 687 gi.sound (current_player, CHAN_VOICE, gi.soundindex("player/drown1.wav"), 1, ATTN_NORM, 0); 688 else if (rand()&1) 689 gi.sound (current_player, CHAN_VOICE, gi.soundindex("*gurp1.wav"), 1, ATTN_NORM, 0); 690 else 691 gi.sound (current_player, CHAN_VOICE, gi.soundindex("*gurp2.wav"), 1, ATTN_NORM, 0); 692 693 current_player->pain_debounce_time = level.time; 694 695 T_Damage (current_player, world, world, vec3_origin, current_player->s.origin, vec3_origin, current_player->dmg, 0, DAMAGE_NO_ARMOR, MOD_WATER); 696 } 697 } 698 } 699 else 700 { 701 current_player->air_finished = level.time + 12; 702 current_player->dmg = 2; 703 } 704 705 // 706 // check for sizzle damage 707 // 708 if (waterlevel && (current_player->watertype&(CONTENTS_LAVA|CONTENTS_SLIME)) ) 709 { 710 if (current_player->watertype & CONTENTS_LAVA) 711 { 712 if (current_player->health > 0 713 && current_player->pain_debounce_time <= level.time 714 && current_client->invincible_framenum < level.framenum) 715 { 716 if (rand()&1) 717 gi.sound (current_player, CHAN_VOICE, gi.soundindex("player/burn1.wav"), 1, ATTN_NORM, 0); 718 else 719 gi.sound (current_player, CHAN_VOICE, gi.soundindex("player/burn2.wav"), 1, ATTN_NORM, 0); 720 current_player->pain_debounce_time = level.time + 1; 721 } 722 723 if (envirosuit) // take 1/3 damage with envirosuit 724 T_Damage (current_player, world, world, vec3_origin, current_player->s.origin, vec3_origin, 1*waterlevel, 0, 0, MOD_LAVA); 725 else 726 T_Damage (current_player, world, world, vec3_origin, current_player->s.origin, vec3_origin, 3*waterlevel, 0, 0, MOD_LAVA); 727 } 728 729 if (current_player->watertype & CONTENTS_SLIME) 730 { 731 if (!envirosuit) 732 { // no damage from slime with envirosuit 733 T_Damage (current_player, world, world, vec3_origin, current_player->s.origin, vec3_origin, 1*waterlevel, 0, 0, MOD_SLIME); 734 } 735 } 736 } 737 } 738 739 740 /* 741 =============== 742 G_SetClientEffects 743 =============== 744 */ 745 void G_SetClientEffects (edict_t *ent) 746 { 747 int pa_type; 748 int remaining; 749 750 ent->s.effects = 0; 751 ent->s.renderfx = 0; 752 753 if (ent->health <= 0 || level.intermissiontime) 754 return; 755 756 if (ent->powerarmor_time > level.time) 757 { 758 pa_type = PowerArmorType (ent); 759 if (pa_type == POWER_ARMOR_SCREEN) 760 { 761 ent->s.effects |= EF_POWERSCREEN; 762 } 763 else if (pa_type == POWER_ARMOR_SHIELD) 764 { 765 ent->s.effects |= EF_COLOR_SHELL; 766 ent->s.renderfx |= RF_SHELL_GREEN; 767 } 768 } 769 770 if (ent->client->quad_framenum > level.framenum) 771 { 772 remaining = ent->client->quad_framenum - level.framenum; 773 if (remaining > 30 || (remaining & 4) ) 774 ent->s.effects |= EF_QUAD; 775 } 776 777 if (ent->client->invincible_framenum > level.framenum) 778 { 779 remaining = ent->client->invincible_framenum - level.framenum; 780 if (remaining > 30 || (remaining & 4) ) 781 ent->s.effects |= EF_PENT; 782 } 783 784 // show cheaters!!! 785 if (ent->flags & FL_GODMODE) 786 { 787 ent->s.effects |= EF_COLOR_SHELL; 788 ent->s.renderfx |= (RF_SHELL_RED|RF_SHELL_GREEN|RF_SHELL_BLUE); 789 } 790 } 791 792 793 /* 794 =============== 795 G_SetClientEvent 796 =============== 797 */ 798 void G_SetClientEvent (edict_t *ent) 799 { 800 if (ent->s.event) 801 return; 802 803 if ( ent->groundentity && xyspeed > 225) 804 { 805 if ( (int)(current_client->bobtime+bobmove) != bobcycle ) 806 ent->s.event = EV_FOOTSTEP; 807 } 808 } 809 810 /* 811 =============== 812 G_SetClientSound 813 =============== 814 */ 815 void G_SetClientSound (edict_t *ent) 816 { 817 char *weap; 818 819 if (ent->client->pers.game_helpchanged != game.helpchanged) 820 { 821 ent->client->pers.game_helpchanged = game.helpchanged; 822 ent->client->pers.helpchanged = 1; 823 } 824 825 // help beep (no more than three times) 826 if (ent->client->pers.helpchanged && ent->client->pers.helpchanged <= 3 && !(level.framenum&63) ) 827 { 828 ent->client->pers.helpchanged++; 829 gi.sound (ent, CHAN_VOICE, gi.soundindex ("misc/pc_up.wav"), 1, ATTN_STATIC, 0); 830 } 831 832 833 if (ent->client->pers.weapon) 834 weap = ent->client->pers.weapon->classname; 835 else 836 weap = ""; 837 838 if (ent->waterlevel && (ent->watertype&(CONTENTS_LAVA|CONTENTS_SLIME)) ) 839 ent->s.sound = snd_fry; 840 else if (strcmp(weap, "weapon_railgun") == 0) 841 ent->s.sound = gi.soundindex("weapons/rg_hum.wav"); 842 else if (strcmp(weap, "weapon_bfg") == 0) 843 ent->s.sound = gi.soundindex("weapons/bfg_hum.wav"); 844 else if (ent->client->weapon_sound) 845 ent->s.sound = ent->client->weapon_sound; 846 else 847 ent->s.sound = 0; 848 } 849 850 /* 851 =============== 852 G_SetClientFrame 853 =============== 854 */ 855 void G_SetClientFrame (edict_t *ent) 856 { 857 gclient_t *client; 858 qboolean duck, run; 859 860 if (ent->s.modelindex != 255) 861 return; // not in the player model 862 863 client = ent->client; 864 865 if (client->ps.pmove.pm_flags & PMF_DUCKED) 866 duck = true; 867 else 868 duck = false; 869 if (xyspeed) 870 run = true; 871 else 872 run = false; 873 874 // check for stand/duck and stop/go transitions 875 if (duck != client->anim_duck && client->anim_priority < ANIM_DEATH) 876 goto newanim; 877 if (run != client->anim_run && client->anim_priority == ANIM_BASIC) 878 goto newanim; 879 if (!ent->groundentity && client->anim_priority <= ANIM_WAVE) 880 goto newanim; 881 882 if(client->anim_priority == ANIM_REVERSE) 883 { 884 if(ent->s.frame > client->anim_end) 885 { 886 ent->s.frame--; 887 return; 888 } 889 } 890 else if (ent->s.frame < client->anim_end) 891 { // continue an animation 892 ent->s.frame++; 893 return; 894 } 895 896 if (client->anim_priority == ANIM_DEATH) 897 return; // stay there 898 if (client->anim_priority == ANIM_JUMP) 899 { 900 if (!ent->groundentity) 901 return; // stay there 902 ent->client->anim_priority = ANIM_WAVE; 903 ent->s.frame = FRAME_jump3; 904 ent->client->anim_end = FRAME_jump6; 905 return; 906 } 907 908 newanim: 909 // return to either a running or standing frame 910 client->anim_priority = ANIM_BASIC; 911 client->anim_duck = duck; 912 client->anim_run = run; 913 914 if (!ent->groundentity) 915 { 916 client->anim_priority = ANIM_JUMP; 917 if (ent->s.frame != FRAME_jump2) 918 ent->s.frame = FRAME_jump1; 919 client->anim_end = FRAME_jump2; 920 } 921 else if (run) 922 { // running 923 if (duck) 924 { 925 ent->s.frame = FRAME_crwalk1; 926 client->anim_end = FRAME_crwalk6; 927 } 928 else 929 { 930 ent->s.frame = FRAME_run1; 931 client->anim_end = FRAME_run6; 932 } 933 } 934 else 935 { // standing 936 if (duck) 937 { 938 ent->s.frame = FRAME_crstnd01; 939 client->anim_end = FRAME_crstnd19; 940 } 941 else 942 { 943 ent->s.frame = FRAME_stand01; 944 client->anim_end = FRAME_stand40; 945 } 946 } 947 } 948 949 950 /* 951 ================= 952 ClientEndServerFrame 953 954 Called for each player at the end of the server frame 955 and right after spawning 956 ================= 957 */ 958 void ClientEndServerFrame (edict_t *ent) 959 { 960 float bobtime; 961 int i; 962 963 current_player = ent; 964 current_client = ent->client; 965 966 // 967 // If the origin or velocity have changed since ClientThink(), 968 // update the pmove values. This will happen when the client 969 // is pushed by a bmodel or kicked by an explosion. 970 // 971 // If it wasn't updated here, the view position would lag a frame 972 // behind the body position when pushed -- "sinking into plats" 973 // 974 for (i=0 ; i<3 ; i++) 975 { 976 current_client->ps.pmove.origin[i] = ent->s.origin[i]*8.0; 977 current_client->ps.pmove.velocity[i] = ent->velocity[i]*8.0; 978 } 979 980 // 981 // If the end of unit layout is displayed, don't give 982 // the player any normal movement attributes 983 // 984 if (level.intermissiontime) 985 { 986 // FIXME: add view drifting here? 987 current_client->ps.blend[3] = 0; 988 current_client->ps.fov = 90; 989 G_SetStats (ent); 990 return; 991 } 992 993 AngleVectors (ent->client->v_angle, forward, right, up); 994 995 // burn from lava, etc 996 P_WorldEffects (); 997 998 // 999 // set model angles from view angles so other things in 1000 // the world can tell which direction you are looking 1001 // 1002 if (ent->client->v_angle[PITCH] > 180) 1003 ent->s.angles[PITCH] = (-360 + ent->client->v_angle[PITCH])/3; 1004 else 1005 ent->s.angles[PITCH] = ent->client->v_angle[PITCH]/3; 1006 ent->s.angles[YAW] = ent->client->v_angle[YAW]; 1007 ent->s.angles[ROLL] = 0; 1008 ent->s.angles[ROLL] = SV_CalcRoll (ent->s.angles, ent->velocity)*4; 1009 1010 // 1011 // calculate speed and cycle to be used for 1012 // all cyclic walking effects 1013 // 1014 xyspeed = sqrt(ent->velocity[0]*ent->velocity[0] + ent->velocity[1]*ent->velocity[1]); 1015 1016 if (xyspeed < 5) 1017 { 1018 bobmove = 0; 1019 current_client->bobtime = 0; // start at beginning of cycle again 1020 } 1021 else if (ent->groundentity) 1022 { // so bobbing only cycles when on ground 1023 if (xyspeed > 210) 1024 bobmove = 0.25; 1025 else if (xyspeed > 100) 1026 bobmove = 0.125; 1027 else 1028 bobmove = 0.0625; 1029 } 1030 1031 bobtime = (current_client->bobtime += bobmove); 1032 1033 if (current_client->ps.pmove.pm_flags & PMF_DUCKED) 1034 bobtime *= 4; 1035 1036 bobcycle = (int)bobtime; 1037 bobfracsin = fabs(sin(bobtime*M_PI)); 1038 1039 // detect hitting the floor 1040 P_FallingDamage (ent); 1041 1042 // apply all the damage taken this frame 1043 P_DamageFeedback (ent); 1044 1045 // determine the view offsets 1046 SV_CalcViewOffset (ent); 1047 1048 // determine the gun offsets 1049 SV_CalcGunOffset (ent); 1050 1051 // determine the full screen color blend 1052 // must be after viewoffset, so eye contents can be 1053 // accurately determined 1054 // FIXME: with client prediction, the contents 1055 // should be determined by the client 1056 SV_CalcBlend (ent); 1057 1058 // chase cam stuff 1059 if (ent->client->resp.spectator) 1060 G_SetSpectatorStats(ent); 1061 else 1062 G_SetStats (ent); 1063 G_CheckChaseStats(ent); 1064 1065 G_SetClientEvent (ent); 1066 1067 G_SetClientEffects (ent); 1068 1069 G_SetClientSound (ent); 1070 1071 G_SetClientFrame (ent); 1072 1073 VectorCopy (ent->velocity, ent->client->oldvelocity); 1074 VectorCopy (ent->client->ps.viewangles, ent->client->oldviewangles); 1075 1076 // clear weapon kicks 1077 VectorClear (ent->client->kick_origin); 1078 VectorClear (ent->client->kick_angles); 1079 1080 // if the scoreboard is up, update it 1081 if (ent->client->showscores && !(level.framenum & 31) ) 1082 { 1083 DeathmatchScoreboardMessage (ent, ent->enemy); 1084 gi.unicast (ent, false); 1085 } 1086 } 1087