|
|
1.1.1.3 ! root 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: */ 1.1 root 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: 1.1.1.2 root 1058: // chase cam stuff 1059: if (ent->client->resp.spectator) 1060: G_SetSpectatorStats(ent); 1061: else 1062: G_SetStats (ent); 1063: G_CheckChaseStats(ent); 1.1 root 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:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.