p_view.c (27656B)
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; // 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 */ 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 //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 */ 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 */ 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 //ZOID 784 && (level.framenum & 8) 785 //ZOID 786 ) 787 { 788 remaining = ent->client->quad_framenum - level.framenum; 789 if (remaining > 30 || (remaining & 4) ) 790 ent->s.effects |= EF_QUAD; 791 } 792 793 if (ent->client->invincible_framenum > level.framenum 794 //ZOID 795 && (level.framenum & 8) 796 //ZOID 797 ) 798 { 799 remaining = ent->client->invincible_framenum - level.framenum; 800 if (remaining > 30 || (remaining & 4) ) 801 ent->s.effects |= EF_PENT; 802 } 803 804 // show cheaters!!! 805 if (ent->flags & FL_GODMODE) 806 { 807 ent->s.effects |= EF_COLOR_SHELL; 808 ent->s.renderfx |= (RF_SHELL_RED|RF_SHELL_GREEN|RF_SHELL_BLUE); 809 } 810 } 811 812 813 /* 814 =============== 815 G_SetClientEvent 816 =============== 817 */ 818 void G_SetClientEvent (edict_t *ent) 819 { 820 if (ent->s.event) 821 return; 822 823 if ( ent->groundentity && xyspeed > 225) 824 { 825 if ( (int)(current_client->bobtime+bobmove) != bobcycle ) 826 ent->s.event = EV_FOOTSTEP; 827 } 828 } 829 830 /* 831 =============== 832 G_SetClientSound 833 =============== 834 */ 835 void G_SetClientSound (edict_t *ent) 836 { 837 char *weap; 838 839 if (ent->client->resp.game_helpchanged != game.helpchanged) 840 { 841 ent->client->resp.game_helpchanged = game.helpchanged; 842 ent->client->resp.helpchanged = 1; 843 } 844 845 // help beep (no more than three times) 846 if (ent->client->resp.helpchanged && ent->client->resp.helpchanged <= 3 && !(level.framenum&63) ) 847 { 848 ent->client->resp.helpchanged++; 849 gi.sound (ent, CHAN_VOICE, gi.soundindex ("misc/pc_up.wav"), 1, ATTN_STATIC, 0); 850 } 851 852 853 if (ent->client->pers.weapon) 854 weap = ent->client->pers.weapon->classname; 855 else 856 weap = ""; 857 858 if (ent->waterlevel && (ent->watertype&(CONTENTS_LAVA|CONTENTS_SLIME)) ) 859 ent->s.sound = snd_fry; 860 else if (strcmp(weap, "weapon_railgun") == 0) 861 ent->s.sound = gi.soundindex("weapons/rg_hum.wav"); 862 else if (strcmp(weap, "weapon_bfg") == 0) 863 ent->s.sound = gi.soundindex("weapons/bfg_hum.wav"); 864 else if (ent->client->weapon_sound) 865 ent->s.sound = ent->client->weapon_sound; 866 else 867 ent->s.sound = 0; 868 } 869 870 /* 871 =============== 872 G_SetClientFrame 873 =============== 874 */ 875 void G_SetClientFrame (edict_t *ent) 876 { 877 gclient_t *client; 878 qboolean duck, run; 879 880 if (ent->s.modelindex != 255) 881 return; // not in the player model 882 883 client = ent->client; 884 885 if (client->ps.pmove.pm_flags & PMF_DUCKED) 886 duck = true; 887 else 888 duck = false; 889 if (xyspeed) 890 run = true; 891 else 892 run = false; 893 894 // check for stand/duck and stop/go transitions 895 if (duck != client->anim_duck && client->anim_priority < ANIM_DEATH) 896 goto newanim; 897 if (run != client->anim_run && client->anim_priority == ANIM_BASIC) 898 goto newanim; 899 if (!ent->groundentity && client->anim_priority <= ANIM_WAVE) 900 goto newanim; 901 902 if(client->anim_priority == ANIM_REVERSE) 903 { 904 if(ent->s.frame > client->anim_end) 905 { 906 ent->s.frame--; 907 return; 908 } 909 } 910 else if (ent->s.frame < client->anim_end) 911 { // continue an animation 912 ent->s.frame++; 913 return; 914 } 915 916 if (client->anim_priority == ANIM_DEATH) 917 return; // stay there 918 if (client->anim_priority == ANIM_JUMP) 919 { 920 if (!ent->groundentity) 921 return; // stay there 922 ent->client->anim_priority = ANIM_WAVE; 923 ent->s.frame = FRAME_jump3; 924 ent->client->anim_end = FRAME_jump6; 925 return; 926 } 927 928 newanim: 929 // return to either a running or standing frame 930 client->anim_priority = ANIM_BASIC; 931 client->anim_duck = duck; 932 client->anim_run = run; 933 934 if (!ent->groundentity) 935 { 936 //ZOID: if on grapple, don't go into jump frame, go into standing 937 //frame 938 if (client->ctf_grapple) { 939 ent->s.frame = FRAME_stand01; 940 client->anim_end = FRAME_stand40; 941 } else { 942 //ZOID 943 client->anim_priority = ANIM_JUMP; 944 if (ent->s.frame != FRAME_jump2) 945 ent->s.frame = FRAME_jump1; 946 client->anim_end = FRAME_jump2; 947 } 948 } 949 else if (run) 950 { // running 951 if (duck) 952 { 953 ent->s.frame = FRAME_crwalk1; 954 client->anim_end = FRAME_crwalk6; 955 } 956 else 957 { 958 ent->s.frame = FRAME_run1; 959 client->anim_end = FRAME_run6; 960 } 961 } 962 else 963 { // standing 964 if (duck) 965 { 966 ent->s.frame = FRAME_crstnd01; 967 client->anim_end = FRAME_crstnd19; 968 } 969 else 970 { 971 ent->s.frame = FRAME_stand01; 972 client->anim_end = FRAME_stand40; 973 } 974 } 975 } 976 977 978 /* 979 ================= 980 ClientEndServerFrame 981 982 Called for each player at the end of the server frame 983 and right after spawning 984 ================= 985 */ 986 void ClientEndServerFrame (edict_t *ent) 987 { 988 float bobtime; 989 int i; 990 991 current_player = ent; 992 current_client = ent->client; 993 994 // 995 // If the origin or velocity have changed since ClientThink(), 996 // update the pmove values. This will happen when the client 997 // is pushed by a bmodel or kicked by an explosion. 998 // 999 // If it wasn't updated here, the view position would lag a frame 1000 // behind the body position when pushed -- "sinking into plats" 1001 // 1002 for (i=0 ; i<3 ; i++) 1003 { 1004 current_client->ps.pmove.origin[i] = ent->s.origin[i]*8.0; 1005 current_client->ps.pmove.velocity[i] = ent->velocity[i]*8.0; 1006 } 1007 1008 // 1009 // If the end of unit layout is displayed, don't give 1010 // the player any normal movement attributes 1011 // 1012 if (level.intermissiontime) 1013 { 1014 // FIXME: add view drifting here? 1015 current_client->ps.blend[3] = 0; 1016 current_client->ps.fov = 90; 1017 G_SetStats (ent); 1018 return; 1019 } 1020 1021 AngleVectors (ent->client->v_angle, forward, right, up); 1022 1023 // burn from lava, etc 1024 P_WorldEffects (); 1025 1026 // 1027 // set model angles from view angles so other things in 1028 // the world can tell which direction you are looking 1029 // 1030 if (ent->client->v_angle[PITCH] > 180) 1031 ent->s.angles[PITCH] = (-360 + ent->client->v_angle[PITCH])/3; 1032 else 1033 ent->s.angles[PITCH] = ent->client->v_angle[PITCH]/3; 1034 ent->s.angles[YAW] = ent->client->v_angle[YAW]; 1035 ent->s.angles[ROLL] = 0; 1036 ent->s.angles[ROLL] = SV_CalcRoll (ent->s.angles, ent->velocity)*4; 1037 1038 // 1039 // calculate speed and cycle to be used for 1040 // all cyclic walking effects 1041 // 1042 xyspeed = sqrt(ent->velocity[0]*ent->velocity[0] + ent->velocity[1]*ent->velocity[1]); 1043 1044 if (xyspeed < 5) 1045 { 1046 bobmove = 0; 1047 current_client->bobtime = 0; // start at beginning of cycle again 1048 } 1049 else if (ent->groundentity) 1050 { // so bobbing only cycles when on ground 1051 if (xyspeed > 210) 1052 bobmove = 0.25; 1053 else if (xyspeed > 100) 1054 bobmove = 0.125; 1055 else 1056 bobmove = 0.0625; 1057 } 1058 1059 bobtime = (current_client->bobtime += bobmove); 1060 1061 if (current_client->ps.pmove.pm_flags & PMF_DUCKED) 1062 bobtime *= 4; 1063 1064 bobcycle = (int)bobtime; 1065 bobfracsin = fabs(sin(bobtime*M_PI)); 1066 1067 // detect hitting the floor 1068 P_FallingDamage (ent); 1069 1070 // apply all the damage taken this frame 1071 P_DamageFeedback (ent); 1072 1073 // determine the view offsets 1074 SV_CalcViewOffset (ent); 1075 1076 // determine the gun offsets 1077 SV_CalcGunOffset (ent); 1078 1079 // determine the full screen color blend 1080 // must be after viewoffset, so eye contents can be 1081 // accurately determined 1082 // FIXME: with client prediction, the contents 1083 // should be determined by the client 1084 SV_CalcBlend (ent); 1085 1086 //ZOID 1087 if (!ent->client->chase_target) 1088 //ZOID 1089 G_SetStats (ent); 1090 1091 //ZOID 1092 //update chasecam follower stats 1093 for (i = 1; i <= maxclients->value; i++) { 1094 edict_t *e = g_edicts + i; 1095 if (!e->inuse || e->client->chase_target != ent) 1096 continue; 1097 memcpy(e->client->ps.stats, 1098 ent->client->ps.stats, 1099 sizeof(ent->client->ps.stats)); 1100 e->client->ps.stats[STAT_LAYOUTS] = 1; 1101 break; 1102 } 1103 //ZOID 1104 1105 1106 G_SetClientEvent (ent); 1107 1108 G_SetClientEffects (ent); 1109 1110 G_SetClientSound (ent); 1111 1112 G_SetClientFrame (ent); 1113 1114 VectorCopy (ent->velocity, ent->client->oldvelocity); 1115 VectorCopy (ent->client->ps.viewangles, ent->client->oldviewangles); 1116 1117 // clear weapon kicks 1118 VectorClear (ent->client->kick_origin); 1119 VectorClear (ent->client->kick_angles); 1120 1121 // if the scoreboard is up, update it 1122 if (ent->client->showscores && !(level.framenum & 31) ) 1123 { 1124 //ZOID 1125 if (ent->client->menu) { 1126 PMenu_Do_Update(ent); 1127 ent->client->menudirty = false; 1128 ent->client->menutime = level.time; 1129 } else 1130 //ZOID 1131 DeathmatchScoreboardMessage (ent, ent->enemy); 1132 gi.unicast (ent, false); 1133 } 1134 } 1135