home *** CD-ROM | disk | FTP | other *** search
/ Gambler 19 / GAMBLERCD19.BIN / UTILS / 3D / BRONIE / DUAL_LAU.ZIP / src / p_view.c < prev    next >
C/C++ Source or Header  |  1997-11-26  |  25KB  |  1,044 lines

  1.  
  2. #include "g_local.h"
  3. #include "m_player.h"
  4.  
  5.  
  6.  
  7. static    edict_t        *current_player;
  8. static    gclient_t    *current_client;
  9.  
  10. static    vec3_t    forward, right, up;
  11. float    xyspeed;
  12.  
  13. float    bobmove;
  14. int        bobcycle;        // odd cycles are right foot going forward
  15. float    bobfracsin;        // sin(bobfrac*M_PI)
  16.  
  17. /*
  18. ===============
  19. SV_CalcRoll
  20.  
  21. ===============
  22. */
  23. float SV_CalcRoll (vec3_t angles, vec3_t velocity)
  24. {
  25.     float    sign;
  26.     float    side;
  27.     float    value;
  28.     
  29.     side = DotProduct (velocity, right);
  30.     sign = side < 0 ? -1 : 1;
  31.     side = fabs(side);
  32.     
  33.     value = sv_rollangle->value;
  34.  
  35.     if (side < sv_rollspeed->value)
  36.         side = side * value / sv_rollspeed->value;
  37.     else
  38.         side = value;
  39.     
  40.     return side*sign;
  41.     
  42. }
  43.  
  44.  
  45. /*
  46. ===============
  47. P_DamageFeedback
  48.  
  49. Handles color blends and view kicks
  50. ===============
  51. */
  52. void P_DamageFeedback (edict_t *player)
  53. {
  54.     gclient_t    *client;
  55.     float    side;
  56.     float    realcount, count, kick;
  57.     vec3_t    v;
  58.     int        r, l;
  59.     static    vec3_t    power_color = {0.0, 1.0, 0.0};
  60.     static    vec3_t    acolor = {1.0, 1.0, 1.0};
  61.     static    vec3_t    bcolor = {1.0, 0.0, 0.0};
  62.  
  63.     client = player->client;
  64.  
  65.     // flash the backgrounds behind the status numbers
  66.     client->ps.stats[STAT_FLASHES] = 0;
  67.     if (client->damage_blood)
  68.         client->ps.stats[STAT_FLASHES] |= 1;
  69.     if (client->damage_armor)
  70.         client->ps.stats[STAT_FLASHES] |= 2;
  71.  
  72.     // total points of damage shot at the player this frame
  73.     count = (client->damage_blood + client->damage_armor + client->damage_parmor);
  74.     if (count == 0)
  75.         return;        // didn't take any damage
  76.  
  77.     // start a pain animation
  78.     if (client->anim_priority < ANIM_PAIN)
  79.     {
  80.         static int        i;
  81.  
  82.         client->anim_priority = ANIM_PAIN;
  83.         if (client->ps.pmove.pm_flags & PMF_DUCKED)
  84.         {
  85.             player->s.frame = FRAME_crpain1-1;
  86.             client->anim_end = FRAME_crpain4;
  87.         }
  88.         else
  89.         {
  90.             i = (i+1)%3;
  91.             switch (i)
  92.             {
  93.             case 0:
  94.                 player->s.frame = FRAME_pain101-1;
  95.                 client->anim_end = FRAME_pain104;
  96.                 break;
  97.             case 1:
  98.                 player->s.frame = FRAME_pain201-1;
  99.                 client->anim_end = FRAME_pain204;
  100.                 break;
  101.             case 2:
  102.                 player->s.frame = FRAME_pain301-1;
  103.                 client->anim_end = FRAME_pain304;
  104.                 break;
  105.             }
  106.         }
  107.     }
  108.  
  109.     realcount = count;
  110.     if (count < 10)
  111.         count = 10;    // allways make a visible effect
  112.  
  113.     // play an apropriate pain sound
  114.     if (level.time > player->pain_debounce_time)
  115.     {
  116.         r = 1 + (rand()&1);
  117.         player->pain_debounce_time = level.time + 0.7;
  118.         if (player->health < 25)
  119.             l = 25;
  120.         else if (player->health < 50)
  121.             l = 50;
  122.         else if (player->health < 75)
  123.             l = 75;
  124.         else
  125.             l = 100;
  126.         gi.sound (player, CHAN_VOICE, SexedSoundIndex(player, va("pain%i_%i", l, r)), 1, ATTN_NORM, 0);
  127.     }
  128.  
  129.     // the total alpha of the blend is allways proportional to count
  130.     if (client->damage_alpha < 0)
  131.         client->damage_alpha = 0;
  132.     client->damage_alpha += count*0.01;
  133.     if (client->damage_alpha < 0.2)
  134.         client->damage_alpha = 0.2;
  135.     if (client->damage_alpha > 0.6)
  136.         client->damage_alpha = 0.6;        // don't go too saturated
  137.  
  138.     // the color of the blend will vary based on how much was absorbed
  139.     // by different armors
  140.     VectorClear (v);
  141.     if (client->damage_parmor)
  142.         VectorMA (v, (float)client->damage_parmor/realcount, power_color, v);
  143.     if (client->damage_armor)
  144.         VectorMA (v, (float)client->damage_armor/realcount,  acolor, v);
  145.     if (client->damage_blood)
  146.         VectorMA (v, (float)client->damage_blood/realcount,  bcolor, v);
  147.     VectorCopy (v, client->damage_blend);
  148.  
  149.  
  150.     //
  151.     // calculate view angle kicks
  152.     //
  153.     kick = abs(client->damage_knockback);
  154.     if (kick && player->health > 0)    // kick of 0 means no view adjust at all
  155.     {
  156.         kick = kick * 100 / player->health;
  157.  
  158.         if (kick < count*0.5)
  159.             kick = count*0.5;
  160.         if (kick > 50)
  161.             kick = 50;
  162.  
  163.         VectorSubtract (client->damage_from, player->s.origin, v);
  164.         VectorNormalize (v);
  165.         
  166.         side = DotProduct (v, right);
  167.         client->v_dmg_roll = kick*side*0.3;
  168.         
  169.         side = -DotProduct (v, forward);
  170.         client->v_dmg_pitch = kick*side*0.3;
  171.  
  172.         client->v_dmg_time = level.time + DAMAGE_TIME;
  173.     }
  174.  
  175.     //
  176.     // clear totals
  177.     //
  178.     client->damage_blood = 0;
  179.     client->damage_armor = 0;
  180.     client->damage_parmor = 0;
  181.     client->damage_knockback = 0;
  182. }
  183.  
  184.  
  185.  
  186.  
  187. /*
  188. ===============
  189. SV_CalcViewOffset
  190.  
  191. Auto pitching on slopes?
  192.  
  193.   fall from 128: 400 = 160000
  194.   fall from 256: 580 = 336400
  195.   fall from 384: 720 = 518400
  196.   fall from 512: 800 = 640000
  197.   fall from 640: 960 = 
  198.  
  199.   damage = deltavelocity*deltavelocity  * 0.0001
  200.  
  201. ===============
  202. */
  203. void SV_CalcViewOffset (edict_t *ent)
  204. {
  205.     float        *angles;
  206.     float        bob;
  207.     float        ratio;
  208.     float        delta;
  209.     vec3_t        v;
  210.  
  211.  
  212. //===================================
  213.  
  214.     // base angles
  215.     angles = ent->client->ps.kick_angles;
  216.  
  217.     // if dead, fix the angle and don't add any kick
  218.     if (ent->deadflag)
  219.     {
  220.         VectorClear (angles);
  221.  
  222.         ent->client->ps.viewangles[ROLL] = 40;
  223.         ent->client->ps.viewangles[PITCH] = -15;
  224.         ent->client->ps.viewangles[YAW] = ent->client->killer_yaw;
  225.     }
  226.     else
  227.     {
  228.         // add angles based on weapon kick
  229.  
  230.         VectorCopy (ent->client->kick_angles, angles);
  231.  
  232.         // add angles based on damage kick
  233.  
  234.         ratio = (ent->client->v_dmg_time - level.time) / DAMAGE_TIME;
  235.         if (ratio < 0)
  236.         {
  237.             ratio = 0;
  238.             ent->client->v_dmg_pitch = 0;
  239.             ent->client->v_dmg_roll = 0;
  240.         }
  241.         angles[PITCH] += ratio * ent->client->v_dmg_pitch;
  242.         angles[ROLL] += ratio * ent->client->v_dmg_roll;
  243.  
  244.         // add pitch based on fall kick
  245.  
  246.         ratio = (ent->client->fall_time - level.time) / FALL_TIME;
  247.         if (ratio < 0)
  248.             ratio = 0;
  249.         angles[PITCH] += ratio * ent->client->fall_value;
  250.  
  251.         // add angles based on velocity
  252.  
  253.         delta = DotProduct (ent->velocity, forward);
  254.         angles[PITCH] += delta*run_pitch->value;
  255.         
  256.         delta = DotProduct (ent->velocity, right);
  257.         angles[ROLL] += delta*run_roll->value;
  258.  
  259.         // add angles based on bob
  260.  
  261.         delta = bobfracsin * bob_pitch->value * xyspeed;
  262.         if (ent->client->ps.pmove.pm_flags & PMF_DUCKED)
  263.             delta *= 6;        // crouching
  264.         angles[PITCH] += delta;
  265.         delta = bobfracsin * bob_roll->value * xyspeed;
  266.         if (ent->client->ps.pmove.pm_flags & PMF_DUCKED)
  267.             delta *= 6;        // crouching
  268.         if (bobcycle & 1)
  269.             delta = -delta;
  270.         angles[ROLL] += delta;
  271.     }
  272.  
  273. //===================================
  274.  
  275.     // base origin
  276.  
  277.     VectorClear (v);
  278.  
  279.     // add view height
  280.  
  281.     v[2] += ent->viewheight;
  282.  
  283.     // add fall height
  284.  
  285.     ratio = (ent->client->fall_time - level.time) / FALL_TIME;
  286.     if (ratio < 0)
  287.         ratio = 0;
  288.     v[2] -= ratio * ent->client->fall_value * 0.4;
  289.  
  290.     // add bob height
  291.  
  292.     bob = bobfracsin * xyspeed * bob_up->value;
  293.     if (bob > 6)
  294.         bob = 6;
  295.     //gi.DebugGraph (bob *2, 255);
  296.     v[2] += bob;
  297.  
  298.     // add kick offset
  299.  
  300.     VectorAdd (v, ent->client->kick_origin, v);
  301.  
  302.     // absolutely bound offsets
  303.     // so the view can never be outside the player box
  304.  
  305.     if (v[0] < -14)
  306.         v[0] = -14;
  307.     else if (v[0] > 14)
  308.         v[0] = 14;
  309.     if (v[1] < -14)
  310.         v[1] = -14;
  311.     else if (v[1] > 14)
  312.         v[1] = 14;
  313.     if (v[2] < -22)
  314.         v[2] = -22;
  315.     else if (v[2] > 30)
  316.         v[2] = 30;
  317.  
  318.     VectorCopy (v, ent->client->ps.viewoffset);
  319. }
  320.  
  321. /*
  322. ==============
  323. SV_CalcGunOffset
  324. ==============
  325. */
  326. void SV_CalcGunOffset (edict_t *ent)
  327. {
  328.     int        i;
  329.     float    delta;
  330.  
  331.     // gun angles from bobbing
  332.     ent->client->ps.gunangles[ROLL] = xyspeed * bobfracsin * 0.005;
  333.     ent->client->ps.gunangles[YAW] = xyspeed * bobfracsin * 0.01;
  334.     if (bobcycle & 1)
  335.     {
  336.         ent->client->ps.gunangles[ROLL] = -ent->client->ps.gunangles[ROLL];
  337.         ent->client->ps.gunangles[YAW] = -ent->client->ps.gunangles[YAW];
  338.     }
  339.  
  340.     ent->client->ps.gunangles[PITCH] = xyspeed * bobfracsin * 0.005;
  341.  
  342.     // gun angles from delta movement
  343.     for (i=0 ; i<3 ; i++)
  344.     {
  345.         delta = ent->client->oldviewangles[i] - ent->client->ps.viewangles[i];
  346.         if (delta > 180)
  347.             delta -= 360;
  348.         if (delta < -180)
  349.             delta += 360;
  350.         if (delta > 45)
  351.             delta = 45;
  352.         if (delta < -45)
  353.             delta = -45;
  354.         if (i == YAW)
  355.             ent->client->ps.gunangles[ROLL] += 0.1*delta;
  356.         ent->client->ps.gunangles[i] += 0.2 * delta;
  357.     }
  358.  
  359.     // gun height
  360.     VectorClear (ent->client->ps.gunoffset);
  361. //    ent->ps->gunorigin[2] += bob;
  362.  
  363.     // gun_x / gun_y / gun_z are development tools
  364.     for (i=0 ; i<3 ; i++)
  365.     {
  366.         ent->client->ps.gunoffset[i] += forward[i]*(gun_y->value);
  367.         ent->client->ps.gunoffset[i] += right[i]*gun_x->value;
  368.         ent->client->ps.gunoffset[i] += up[i]* (-gun_z->value);
  369.     }
  370. }
  371.  
  372.  
  373. /*
  374. =============
  375. SV_AddBlend
  376. =============
  377. */
  378. void SV_AddBlend (float r, float g, float b, float a, float *v_blend)
  379. {
  380.     float    a2, a3;
  381.  
  382.     if (a <= 0)
  383.         return;
  384.     a2 = v_blend[3] + (1-v_blend[3])*a;    // new total alpha
  385.     a3 = v_blend[3]/a2;        // fraction of color from old
  386.  
  387.     v_blend[0] = v_blend[0]*a3 + r*(1-a3);
  388.     v_blend[1] = v_blend[1]*a3 + g*(1-a3);
  389.     v_blend[2] = v_blend[2]*a3 + b*(1-a3);
  390.     v_blend[3] = a2;
  391. }
  392.  
  393.  
  394. /*
  395. =============
  396. SV_CalcBlend
  397. =============
  398. */
  399. void SV_CalcBlend (edict_t *ent)
  400. {
  401.     int        contents;
  402.     vec3_t    vieworg;
  403.     int        remaining;
  404.  
  405.     ent->client->ps.blend[0] = ent->client->ps.blend[1] = 
  406.         ent->client->ps.blend[2] = ent->client->ps.blend[3] = 0;
  407.  
  408.     // add for contents
  409.     VectorAdd (ent->s.origin, ent->client->ps.viewoffset, vieworg);
  410.     contents = gi.pointcontents (vieworg);
  411.     if (contents & (CONTENTS_LAVA|CONTENTS_SLIME|CONTENTS_WATER) )
  412.         ent->client->ps.rdflags |= RDF_UNDERWATER;
  413.     else
  414.         ent->client->ps.rdflags &= ~RDF_UNDERWATER;
  415.  
  416.     if (contents & (CONTENTS_SOLID|CONTENTS_LAVA))
  417.         SV_AddBlend (1.0, 0.3, 0.0, 0.6, ent->client->ps.blend);
  418.     else if (contents & CONTENTS_SLIME)
  419.         SV_AddBlend (0.0, 0.1, 0.05, 0.6, ent->client->ps.blend);
  420.     else if (contents & CONTENTS_WATER)
  421.         SV_AddBlend (0.5, 0.3, 0.2, 0.5, ent->client->ps.blend);
  422.  
  423.     // add for powerups
  424.     if (ent->client->quad_framenum > level.framenum)
  425.     {
  426.         remaining = ent->client->quad_framenum - level.framenum;
  427.         if (remaining == 30)    // beginning to fade
  428.             gi.sound(ent, CHAN_ITEM, gi.soundindex("items/damage2.wav"), 1, ATTN_NORM, 0);
  429.         if (remaining > 30 || (remaining & 4) )
  430.             SV_AddBlend (0, 0, 1, 0.08, ent->client->ps.blend);
  431.     }
  432.     else if (ent->client->invincible_framenum > level.framenum)
  433.     {
  434.         remaining = ent->client->invincible_framenum - level.framenum;
  435.         if (remaining == 30)    // beginning to fade
  436.             gi.sound(ent, CHAN_ITEM, gi.soundindex("items/protect2.wav"), 1, ATTN_NORM, 0);
  437.         if (remaining > 30 || (remaining & 4) )
  438.             SV_AddBlend (1, 1, 0, 0.08, ent->client->ps.blend);
  439.     }
  440.     else if (ent->client->enviro_framenum > level.framenum)
  441.     {
  442.         remaining = ent->client->enviro_framenum - level.framenum;
  443.         if (remaining == 30)    // beginning to fade
  444.             gi.sound(ent, CHAN_ITEM, gi.soundindex("items/airout.wav"), 1, ATTN_NORM, 0);
  445.         if (remaining > 30 || (remaining & 4) )
  446.             SV_AddBlend (0, 1, 0, 0.08, ent->client->ps.blend);
  447.     }
  448.     else if (ent->client->breather_framenum > level.framenum)
  449.     {
  450.         remaining = ent->client->breather_framenum - level.framenum;
  451.         if (remaining == 30)    // beginning to fade
  452.             gi.sound(ent, CHAN_ITEM, gi.soundindex("items/airout.wav"), 1, ATTN_NORM, 0);
  453.         if (remaining > 30 || (remaining & 4) )
  454.             SV_AddBlend (0.4, 1, 0.4, 0.04, ent->client->ps.blend);
  455.     }
  456.  
  457.     // add for damage
  458.     if (ent->client->damage_alpha > 0)
  459.         SV_AddBlend (ent->client->damage_blend[0],ent->client->damage_blend[1]
  460.         ,ent->client->damage_blend[2], ent->client->damage_alpha, ent->client->ps.blend);
  461.  
  462.     if (ent->client->bonus_alpha > 0)
  463.         SV_AddBlend (0.85, 0.7, 0.3, ent->client->bonus_alpha, ent->client->ps.blend);
  464.  
  465.     // drop the damage value
  466.     ent->client->damage_alpha -= 0.06;
  467.     if (ent->client->damage_alpha < 0)
  468.         ent->client->damage_alpha = 0;
  469.  
  470.     // drop the bonus value
  471.     ent->client->bonus_alpha -= 0.1;
  472.     if (ent->client->bonus_alpha < 0)
  473.         ent->client->bonus_alpha = 0;
  474. }
  475.  
  476.  
  477. /*
  478. =================
  479. P_FallingDamage
  480. =================
  481. */
  482. void P_FallingDamage (edict_t *ent)
  483. {
  484.     float    delta;
  485.     int        damage;
  486.     vec3_t    dir;
  487.  
  488.     if (ent->s.modelindex != 255)
  489.         return;        // not in the player model
  490.  
  491.     if (!ent->groundentity)
  492.         return;
  493.  
  494.     delta = ent->velocity[2] - ent->client->oldvelocity[2];
  495.     delta = delta*delta * 0.0001;
  496.  
  497.     // never take falling damage if completely underwater
  498.     if (ent->waterlevel == 3)
  499.         return;
  500.     if (ent->waterlevel == 2)
  501.         delta *= 0.25;
  502.     if (ent->waterlevel == 1)
  503.         delta *= 0.5;
  504.  
  505.     if (delta < 1)
  506.         return;
  507.  
  508.     if (delta < 15)
  509.     {
  510.         ent->s.event = EV_FOOTSTEP;
  511.         return;
  512.     }
  513.  
  514.     ent->client->fall_value = delta*0.5;
  515.     if (ent->client->fall_value > 40)
  516.         ent->client->fall_value = 40;
  517.     ent->client->fall_time = level.time + FALL_TIME;
  518.  
  519.     if (delta > 30)
  520.     {
  521.         if (ent->health > 0)
  522.         {
  523.             if (!strcmp (ent->client->pers.sounddir, "player/female"))
  524.             {
  525.                 if (delta >= 55)
  526.                     ent->s.event = EV_FEMALE_FALLFAR;
  527.                 else
  528.                     ent->s.event = EV_FEMALE_FALL;
  529.             }
  530.             else
  531.             {
  532.                 if (delta >= 55)
  533.                     ent->s.event = EV_MALE_FALLFAR;
  534.                 else
  535.                     ent->s.event = EV_MALE_FALL;
  536.             }
  537.         }
  538.         ent->pain_debounce_time = level.time;    // no normal pain sound
  539.         damage = (delta-30)/2;
  540.         if (damage < 1)
  541.             damage = 1;
  542.         VectorSet (dir, 0, 0, 1);
  543.  
  544.         if (!deathmatch->value || !((int)dmflags->value & DF_NO_FALLING) )
  545.             T_Damage (ent, world, world, dir, ent->s.origin, vec3_origin, damage, 0, 0);
  546.     }
  547.     else
  548.     {
  549.         ent->s.event = EV_FALLSHORT;
  550.         return;
  551.     }
  552. }
  553.  
  554.  
  555.  
  556. /*
  557. =============
  558. P_WorldEffects
  559. =============
  560. */
  561. void P_WorldEffects (void)
  562. {
  563.     qboolean    breather;
  564.     qboolean    envirosuit;
  565.     int            waterlevel, old_waterlevel;
  566.  
  567.     if (current_player->movetype == MOVETYPE_NOCLIP)
  568.     {
  569.         current_player->air_finished = level.time + 12;    // don't need air
  570.         return;
  571.     }
  572.  
  573.     waterlevel = current_player->waterlevel;
  574.     old_waterlevel = current_client->old_waterlevel;
  575.     current_client->old_waterlevel = waterlevel;
  576.  
  577.     breather = current_client->breather_framenum > level.framenum;
  578.     envirosuit = current_client->enviro_framenum > level.framenum;
  579.  
  580.     //
  581.     // if just entered a water volume, play a sound
  582.     //
  583.     if (!old_waterlevel && waterlevel)
  584.     {
  585.         PlayerNoise(current_player, current_player->s.origin, PNOISE_SELF);
  586.         if (current_player->watertype & CONTENTS_LAVA)
  587.             gi.sound (current_player, CHAN_BODY, gi.soundindex("player/lava_in.wav"), 1, ATTN_NORM, 0);
  588.         else if (current_player->watertype & CONTENTS_SLIME)
  589.             gi.sound (current_player, CHAN_BODY, gi.soundindex("player/watr_in.wav"), 1, ATTN_NORM, 0);
  590.         else if (current_player->watertype & CONTENTS_WATER)
  591.             gi.sound (current_player, CHAN_BODY, gi.soundindex("player/watr_in.wav"), 1, ATTN_NORM, 0);
  592.         current_player->flags |= FL_INWATER;
  593.  
  594.         // clear damage_debounce, so the pain sound will play immediately
  595.         current_player->damage_debounce_time = level.time - 1;
  596.     }
  597.  
  598.     //
  599.     // if just completely exited a water volume, play a sound
  600.     //
  601.     if (old_waterlevel && ! waterlevel)
  602.     {
  603.         PlayerNoise(current_player, current_player->s.origin, PNOISE_SELF);
  604.         gi.sound (current_player, CHAN_BODY, gi.soundindex("player/watr_out.wav"), 1, ATTN_NORM, 0);
  605.         current_player->flags &= ~FL_INWATER;
  606.     }
  607.  
  608.     //
  609.     // check for head just going under water
  610.     //
  611.     if (old_waterlevel != 3 && waterlevel == 3)
  612.     {
  613.         gi.sound (current_player, CHAN_BODY, gi.soundindex("player/watr_un.wav"), 1, ATTN_NORM, 0);
  614.     }
  615.  
  616.     //
  617.     // check for head just coming out of water
  618.     //
  619.     if (old_waterlevel == 3 && waterlevel != 3)
  620.     {
  621.         if (current_player->air_finished < level.time)
  622.         {    // gasp for air
  623.             gi.sound (current_player, CHAN_VOICE, gi.soundindex("player/gasp1.wav"), 1, ATTN_NORM, 0);
  624.             PlayerNoise(current_player, current_player->s.origin, PNOISE_SELF);
  625.         }
  626.         else  if (current_player->air_finished < level.time + 11)
  627.         {    // just break surface
  628.             gi.sound (current_player, CHAN_VOICE, gi.soundindex("player/gasp2.wav"), 1, ATTN_NORM, 0);
  629.         }
  630.     }
  631.  
  632.     //
  633.     // check for drowning
  634.     //
  635.     if (waterlevel == 3)
  636.     {
  637.         // breather or envirosuit give air
  638.         if (breather || envirosuit)
  639.         {
  640.             current_player->air_finished = level.time + 10;
  641.  
  642.             if (((int)(current_client->breather_framenum - level.framenum) % 25) == 0)
  643.             {
  644.                 if (!current_client->breather_sound)
  645.                     gi.sound (current_player, CHAN_AUTO, gi.soundindex("player/u_breath1.wav"), 1, ATTN_NORM, 0);
  646.                 else
  647.                     gi.sound (current_player, CHAN_AUTO, gi.soundindex("player/u_breath2.wav"), 1, ATTN_NORM, 0);
  648.                 current_client->breather_sound ^= 1;
  649.                 PlayerNoise(current_player, current_player->s.origin, PNOISE_SELF);
  650.                 //FIXME: release a bubble?
  651.             }
  652.         }
  653.  
  654.         // if out of air, start drowning
  655.         if (current_player->air_finished < level.time)
  656.         {    // drown!
  657.             if (current_player->client->next_drown_time < level.time 
  658.                 && current_player->health > 0)
  659.             {
  660.                 current_player->client->next_drown_time = level.time + 1;
  661.  
  662.                 // take more damage the longer underwater
  663.                 current_player->dmg += 2;
  664.                 if (current_player->dmg > 15)
  665.                     current_player->dmg = 15;
  666.  
  667.                 // play a gurp sound instead of a normal pain sound
  668.                 if (rand()&1)
  669.                     gi.sound (current_player, CHAN_VOICE, SexedSoundIndex(current_player, "gurp1"), 1, ATTN_NORM, 0);
  670.                 else
  671.                     gi.sound (current_player, CHAN_VOICE, SexedSoundIndex(current_player, "gurp2"), 1, ATTN_NORM, 0);
  672.                 current_player->pain_debounce_time = level.time;
  673.  
  674.                 T_Damage (current_player, world, world, vec3_origin, current_player->s.origin, vec3_origin, current_player->dmg, 0, DAMAGE_NO_ARMOR);
  675.             }
  676.         }
  677.     }
  678.     else
  679.     {
  680.         current_player->air_finished = level.time + 12;
  681.         current_player->dmg = 2;
  682.     }
  683.  
  684.     //
  685.     // check for sizzle damage
  686.     //
  687.     if (waterlevel && (current_player->watertype&(CONTENTS_LAVA|CONTENTS_SLIME)) )
  688.     {
  689.         if (current_player->watertype & CONTENTS_LAVA)
  690.         {
  691.             if (current_player->health > 0
  692.                 && current_player->pain_debounce_time <= level.time)
  693.             {
  694.                 if (rand()&1)
  695.                     gi.sound (current_player, CHAN_VOICE, gi.soundindex("player/burn1.wav"), 1, ATTN_NORM, 0);
  696.                 else
  697.                     gi.sound (current_player, CHAN_VOICE, gi.soundindex("player/burn2.wav"), 1, ATTN_NORM, 0);
  698.                 current_player->pain_debounce_time = level.time + 1;
  699.             }
  700.  
  701.             if (envirosuit)    // take 1/3 damage with envirosuit
  702.                 T_Damage (current_player, world, world, vec3_origin, current_player->s.origin, vec3_origin, 1*waterlevel, 0, 0);
  703.             else
  704.                 T_Damage (current_player, world, world, vec3_origin, current_player->s.origin, vec3_origin, 3*waterlevel, 0, 0);
  705.         }
  706.  
  707.         if (current_player->watertype & CONTENTS_SLIME)
  708.         {
  709.             if (!envirosuit)
  710.             {    // no damage from slime with envirosuit
  711.                 T_Damage (current_player, world, world, vec3_origin, current_player->s.origin, vec3_origin, 1*waterlevel, 0, 0);
  712.             }
  713.         }
  714.     }
  715. }
  716.  
  717.  
  718. /*
  719. ===============
  720. G_SetClientEffects
  721. ===============
  722. */
  723. void G_SetClientEffects (edict_t *ent)
  724. {
  725.     int        pa_type;
  726.     int        remaining;
  727.  
  728.     ent->s.effects = 0;
  729.     ent->s.renderfx = 0;
  730.  
  731.     if (ent->health <= 0 || level.intermissiontime)
  732.         return;
  733.  
  734.     if (ent->powerarmor_time > level.time)
  735.     {
  736.         pa_type = PowerArmorType (ent);
  737.         if (pa_type == POWER_ARMOR_SCREEN)
  738.         {
  739.             ent->s.effects |= EF_POWERSCREEN;
  740.         }
  741.         else if (pa_type == POWER_ARMOR_SHIELD)
  742.         {
  743.             ent->s.effects |= EF_COLOR_SHELL;
  744.             ent->s.renderfx |= RF_SHELL_GREEN;
  745.         }
  746.     }
  747.  
  748.     if (ent->client->quad_framenum > level.framenum)
  749.     {
  750.         remaining = ent->client->quad_framenum - level.framenum;
  751.         if (remaining > 30 || (remaining & 4) )
  752.             ent->s.effects |= EF_QUAD;
  753.     }
  754.  
  755.     if (ent->client->invincible_framenum > level.framenum)
  756.     {
  757.         remaining = ent->client->invincible_framenum - level.framenum;
  758.         if (remaining > 30 || (remaining & 4) )
  759.             ent->s.effects |= EF_PENT;
  760.     }
  761.  
  762.     // show cheaters!!!
  763.     if (ent->flags & FL_GODMODE)
  764.     {
  765.         ent->s.effects |= EF_COLOR_SHELL;
  766.         ent->s.renderfx |= (RF_SHELL_RED|RF_SHELL_GREEN|RF_SHELL_BLUE);
  767.     }
  768. }
  769.  
  770.  
  771. /*
  772. ===============
  773. G_SetClientEvent
  774. ===============
  775. */
  776. void G_SetClientEvent (edict_t *ent)
  777. {
  778.     if (ent->s.event)
  779.         return;
  780.  
  781.     if ( ent->groundentity && xyspeed > 225)
  782.     {
  783.         if ( (int)(current_client->bobtime+bobmove) != bobcycle )
  784.             ent->s.event = EV_FOOTSTEP;
  785.     }
  786. }
  787.  
  788. /*
  789. ===============
  790. G_SetClientSound
  791. ===============
  792. */
  793. void G_SetClientSound (edict_t *ent)
  794. {
  795.     char    *weap;
  796.  
  797.     // help beep
  798.     if (game.helpchanged && !(level.framenum&63) )
  799.         gi.sound (ent, CHAN_VOICE, gi.soundindex ("misc/pc_up.wav"), 1, ATTN_STATIC, 0);
  800.  
  801.  
  802.     if (ent->client->pers.weapon)
  803.         weap = ent->client->pers.weapon->classname;
  804.     else
  805.         weap = "";
  806.  
  807.     if (ent->waterlevel && (ent->watertype&(CONTENTS_LAVA|CONTENTS_SLIME)) )
  808.         ent->s.sound = snd_fry;
  809.     else if (strcmp(weap, "weapon_railgun") == 0)
  810.         ent->s.sound = gi.soundindex("weapons/rg_hum.wav");
  811.     else if (strcmp(weap, "weapon_bfg") == 0)
  812.         ent->s.sound = gi.soundindex("weapons/bfg_hum.wav");
  813.     else if (ent->client->weapon_sound)
  814.         ent->s.sound = ent->client->weapon_sound;
  815.     else
  816.         ent->s.sound = 0;
  817. }
  818.  
  819. /*
  820. ===============
  821. G_SetClientFrame
  822. ===============
  823. */
  824. void G_SetClientFrame (edict_t *ent)
  825. {
  826.     gclient_t    *client;
  827.     qboolean    duck, run;
  828.  
  829.     if (ent->s.modelindex != 255)
  830.         return;        // not in the player model
  831.  
  832.     client = ent->client;
  833.  
  834.     if (client->ps.pmove.pm_flags & PMF_DUCKED)
  835.         duck = true;
  836.     else
  837.         duck = false;
  838.     if (xyspeed)
  839.         run = true;
  840.     else
  841.         run = false;
  842.  
  843.     // check for stand/duck and stop/go transitions
  844.     if (duck != client->anim_duck && client->anim_priority < ANIM_DEATH)
  845.         goto newanim;
  846.     if (run != client->anim_run && client->anim_priority == ANIM_BASIC)
  847.         goto newanim;
  848.     if (!ent->groundentity && client->anim_priority <= ANIM_WAVE)
  849.         goto newanim;
  850.  
  851.     if (ent->s.frame < client->anim_end)
  852.     {    // continue an animation
  853.         ent->s.frame++;
  854.         return;
  855.     }
  856.  
  857.     if (client->anim_priority == ANIM_DEATH)
  858.         return;        // stay there
  859.     if (client->anim_priority == ANIM_JUMP)
  860.     {
  861.         if (!ent->groundentity)
  862.             return;        // stay there
  863.         ent->client->anim_priority = ANIM_WAVE;
  864.         ent->s.frame = FRAME_jump3;
  865.         ent->client->anim_end = FRAME_jump6;
  866.         return;
  867.     }
  868.  
  869. newanim:
  870.     // return to either a running or standing frame
  871.     client->anim_priority = ANIM_BASIC;
  872.     client->anim_duck = duck;
  873.     client->anim_run = run;
  874.  
  875.     if (!ent->groundentity)
  876.     {
  877.         client->anim_priority = ANIM_JUMP;
  878.         if (ent->s.frame != FRAME_jump2)
  879.             ent->s.frame = FRAME_jump1;
  880.         client->anim_end = FRAME_jump2;
  881.     }
  882.     else if (run)
  883.     {    // running
  884.         if (duck)
  885.         {
  886.             ent->s.frame = FRAME_crwalk1;
  887.             client->anim_end = FRAME_crwalk6;
  888.         }
  889.         else
  890.         {
  891.             ent->s.frame = FRAME_run1;
  892.             client->anim_end = FRAME_run6;
  893.         }
  894.     }
  895.     else
  896.     {    // standing
  897.         if (duck)
  898.         {
  899.             ent->s.frame = FRAME_crstnd01;
  900.             client->anim_end = FRAME_crstnd19;
  901.         }
  902.         else
  903.         {
  904.             ent->s.frame = FRAME_stand01;
  905.             client->anim_end = FRAME_stand40;
  906.         }
  907.     }
  908. }
  909.  
  910.  
  911. /*
  912. =================
  913. ClientEndServerFrame
  914.  
  915. Called for each player at the end of the server frame
  916. and right after spawning
  917. =================
  918. */
  919. void ClientEndServerFrame (edict_t *ent)
  920. {
  921.     float    bobtime;
  922.     int        i;
  923.  
  924.     current_player = ent;
  925.     current_client = ent->client;
  926.  
  927.     //
  928.     // If the origin or velocity have changed since ClientThink(),
  929.     // update the pmove values.  This will happen when the client
  930.     // is pushed by a bmodel or kicked by an explosion.
  931.     // 
  932.     // If it wasn't updated here, the view position would lag a frame
  933.     // behind the body position when pushed -- "sinking into plats"
  934.     //
  935.     for (i=0 ; i<3 ; i++)
  936.     {
  937.         current_client->ps.pmove.origin[i] = ent->s.origin[i]*8.0;
  938.         current_client->ps.pmove.velocity[i] = ent->velocity[i]*8.0;
  939.     }
  940.  
  941.     //
  942.     // If the end of unit layout is displayed, don't give
  943.     // the player any normal movement attributes
  944.     //
  945.     if (level.intermissiontime)
  946.     {
  947.         // FIXME: add view drifting here?
  948.         current_client->ps.blend[3] = 0;
  949.         current_client->ps.fov = 90;
  950.         G_SetStats (ent);
  951.         return;
  952.     }
  953.  
  954.     AngleVectors (ent->client->v_angle, forward, right, up);
  955.  
  956.     // burn from lava, etc
  957.     P_WorldEffects ();
  958.  
  959.     //
  960.     // set model angles from view angles so other things in
  961.     // the world can tell which direction you are looking
  962.     //
  963.     if (ent->client->v_angle[PITCH] > 180)
  964.         ent->s.angles[PITCH] = (-360 + ent->client->v_angle[PITCH])/3;
  965.     else
  966.         ent->s.angles[PITCH] = ent->client->v_angle[PITCH]/3;
  967.     ent->s.angles[YAW] = ent->client->v_angle[YAW];
  968.     ent->s.angles[ROLL] = 0;
  969.     ent->s.angles[ROLL] = SV_CalcRoll (ent->s.angles, ent->velocity)*4;
  970.  
  971.     //
  972.     // calculate speed and cycle to be used for
  973.     // all cyclic walking effects
  974.     //
  975.     xyspeed = sqrt(ent->velocity[0]*ent->velocity[0] + ent->velocity[1]*ent->velocity[1]);
  976.  
  977.     if (xyspeed < 5)
  978.     {
  979.         bobmove = 0;
  980.         current_client->bobtime = 0;    // start at beginning of cycle again
  981.     }
  982.     else if (ent->groundentity)
  983.     {    // so bobbing only cycles when on ground
  984.         if (xyspeed > 210)
  985.             bobmove = 0.25;
  986.         else if (xyspeed > 100)
  987.             bobmove = 0.125;
  988.         else
  989.             bobmove = 0.0625;
  990.     }
  991.     
  992.     bobtime = (current_client->bobtime += bobmove);
  993.  
  994.     if (current_client->ps.pmove.pm_flags & PMF_DUCKED)
  995.         bobtime *= 4;
  996.  
  997.     bobcycle = (int)bobtime;
  998.     bobfracsin = fabs(sin(bobtime*M_PI));
  999.  
  1000.     // detect hitting the floor
  1001.     P_FallingDamage (ent);
  1002.  
  1003.     // apply all the damage taken this frame
  1004.     P_DamageFeedback (ent);
  1005.  
  1006.     // determine the view offsets
  1007.     SV_CalcViewOffset (ent);
  1008.  
  1009.     // determine the gun offsets
  1010.     SV_CalcGunOffset (ent);
  1011.  
  1012.     // determine the full screen color blend
  1013.     // must be after viewoffset, so eye contents can be
  1014.     // accurately determined
  1015.     // FIXME: with client prediction, the contents
  1016.     // should be determined by the client
  1017.     SV_CalcBlend (ent);
  1018.  
  1019.     G_SetStats (ent);
  1020.  
  1021.     G_SetClientEvent (ent);
  1022.  
  1023.     G_SetClientEffects (ent);
  1024.  
  1025.     G_SetClientSound (ent);
  1026.  
  1027.     G_SetClientFrame (ent);
  1028.  
  1029.     VectorCopy (ent->velocity, ent->client->oldvelocity);
  1030.     VectorCopy (ent->client->ps.viewangles, ent->client->oldviewangles);
  1031.  
  1032.     // clear weapon kicks
  1033.     VectorClear (ent->client->kick_origin);
  1034.     VectorClear (ent->client->kick_angles);
  1035.  
  1036.     // if the scoreboard is up, update it
  1037.     if (ent->client->showscores && deathmatch->value && !(level.framenum & 31) )
  1038.     {
  1039.         DeathmatchScoreboardMessage (ent, ent->enemy);
  1040.         gi.unicast (ent, false);
  1041.     }
  1042. }
  1043.  
  1044.