]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/commitdiff
Merge branch 'terencehill/dynamic_hud' into 'master'
authorMario <zacjardine@y7mail.com>
Wed, 8 Jun 2016 09:31:19 +0000 (09:31 +0000)
committerMario <zacjardine@y7mail.com>
Wed, 8 Jun 2016 09:31:19 +0000 (09:31 +0000)
Dynamic hud

- hud_dynamic_follow: HUD moves around following player's movement (effect shared with cl_followmodel, can be enabled independently from it though)
- hud_dynamic_shake: shake the HUD when hurt

See merge request !293

1  2 
defaultXonotic.cfg
qcsrc/client/autocvars.qh
qcsrc/client/hud/panel/radar.qc
qcsrc/client/hud/panel/weapons.qc
qcsrc/client/main.qc
qcsrc/client/view.qc

diff --combined defaultXonotic.cfg
index 6554bb1a76bccd80cb217e09d2f64ed73101ee9f,a552ad619443233190399894ab9275ef0d501362..dea35012a76a36546bfb922a8247b4ea67c904e4
@@@ -809,7 -809,6 +809,7 @@@ seta menu_tooltips 1 "menu tooltips: 0 
  set menu_picmip_bypass 0 "bypass texture quality enforcement based on system resources, not recommended and may cause crashes!"
  set menu_showboxes 0 "show item bounding boxes (debug)"
  set menu_cvarlist_onlymodified 0 "show only modified cvars in the cvar list"
 +set menu_force_on_disconnection 1 "force to show the menu this number of seconds after you get disconnected (0 to disable)"
  
  r_textbrightness 0.2
  r_textcontrast 0.8
@@@ -853,6 -852,7 +853,7 @@@ seta scoreboard_offset_right 0.15 "how 
  seta scoreboard_offset_vertical 0.05 "how far (by percent) the scoreboard is offset from the top and bottom of the screen"
  seta scoreboard_bg_scale 0.25 "scale for the tiled scoreboard background"
  seta scoreboard_respawntime_decimals 1 "decimal places to show for the respawntime countdown display on the scoreboard"
+ seta scoreboard_dynamichud 0 "apply the dynamic hud effects to the scoreboard"
  
  seta accuracy_color_levels "0 20 100" "accuracy values at which a specified color (accuracy_color<X>) will be used. If your accuracy is between 2 of these values then a mix of the Xth and X+1th colors will be used. You can specify up to 10 values, in increasing order"
  seta accuracy_color0 "1 0 0"
index 086dec33e0cf1eb3c51da170be52c366d736f6df,e63c9e4cd9331b6211c3155fa95ae217f3626f39..3e222c00f88666c364c9296be1c1315f5e9cfb4c
@@@ -193,6 -193,23 +193,23 @@@ string autocvar_hud_dock
  float autocvar_hud_dock_alpha;
  string autocvar_hud_dock_color;
  bool autocvar_hud_dock_color_team;
+ bool autocvar_hud_panel_weapons_dynamichud      = true;
+ bool autocvar_hud_panel_ammo_dynamichud         = true;
+ bool autocvar_hud_panel_powerups_dynamichud     = true;
+ bool autocvar_hud_panel_healtharmor_dynamichud  = true;
+ bool autocvar_hud_panel_notify_dynamichud       = true;
+ bool autocvar_hud_panel_timer_dynamichud        = true;
+ bool autocvar_hud_panel_radar_dynamichud        = true;
+ bool autocvar_hud_panel_score_dynamichud        = true;
+ bool autocvar_hud_panel_racetimer_dynamichud    = true;
+ bool autocvar_hud_panel_vote_dynamichud         = true;
+ bool autocvar_hud_panel_modicons_dynamichud     = true;
+ bool autocvar_hud_panel_pressedkeys_dynamichud  = true;
+ bool autocvar_hud_panel_engineinfo_dynamichud   = true;
+ bool autocvar_hud_panel_infomessages_dynamichud = false;
+ bool autocvar_hud_panel_physics_dynamichud      = true;
+ bool autocvar_hud_panel_centerprint_dynamichud  = true;
+ bool autocvar_hud_panel_itemstime_dynamichud    = true;
  bool autocvar_hud_panel_ammo;
  bool autocvar_hud_panel_ammo_iconalign;
  int autocvar_hud_panel_ammo_maxammo;
@@@ -327,8 -344,6 +344,8 @@@ float autocvar_hud_panel_weapons_label_
  bool autocvar_hud_panel_weapons_onlyowned;
  float autocvar_hud_panel_weapons_noncurrent_alpha = 1;
  float autocvar_hud_panel_weapons_noncurrent_scale = 1;
 +float autocvar_hud_panel_weapons_selection_radius = 0;
 +float autocvar_hud_panel_weapons_selection_speed = 10;
  float autocvar_hud_panel_weapons_timeout;
  int autocvar_hud_panel_weapons_timeout_effect;
  float autocvar_hud_panel_weapons_timeout_fadebgmin;
@@@ -374,6 -389,7 +391,7 @@@ float autocvar_hud_shownames_offset
  string autocvar_hud_skin;
  float autocvar_menu_mouse_speed;
  string autocvar_menu_skin;
+ float autocvar_r_drawviewmodel;
  int autocvar_r_fakelight;
  int autocvar_r_fullbright;
  float autocvar_r_letterbox;
@@@ -400,6 -416,7 +418,7 @@@ float autocvar_scoreboard_offset_left
  float autocvar_scoreboard_offset_right;
  float autocvar_scoreboard_offset_vertical;
  float autocvar_scoreboard_respawntime_decimals;
+ float autocvar_scoreboard_dynamichud = 1;
  bool autocvar_v_flipped;
  float autocvar_vid_conheight;
  float autocvar_vid_conwidth;
@@@ -414,6 -431,7 +433,6 @@@ float autocvar_cl_hitsound_max_pitch = 
  float autocvar_cl_hitsound_nom_damage = 25;
  float autocvar_cl_hitsound_antispam_time;
  int autocvar_cl_eventchase_death = 1;
 -int autocvar_cl_eventchase_nexball = 1;
  vector autocvar_cl_eventchase_generator_viewoffset = '0 0 80';
  float autocvar_cl_eventchase_generator_distance = 400;
  float autocvar_cl_eventchase_distance = 140;
index 8bbcc29e4ed88f7749a53a01e4737edf872ac381,7a8cb96e77a1f7cb0b3a8ee2277f92cd63ae0bf0..1642e41891a8d75eed766cbe826bad92636feaa7
@@@ -275,6 -275,10 +275,10 @@@ void HUD_Radar(
        pos = panel_pos;
        mySize = panel_size;
  
+       if (autocvar_hud_panel_radar_dynamichud)
+               HUD_Scale_Enable();
+       else
+               HUD_Scale_Disable();
        HUD_Panel_DrawBg(1);
        if(panel_bg_padding)
        {
        int color2;
        float scale2d, normalsize, bigsize;
  
-       teamradar_origin2d = pos + 0.5 * mySize;
+       teamradar_origin2d = HUD_Shift(pos + 0.5 * mySize);
        teamradar_size2d = mySize;
  
        if(minimapname == "")
        teamradar_loadcvars();
  
        scale2d = vlen_maxnorm2d(mi_picmax - mi_picmin);
-       teamradar_size2d = mySize;
+       teamradar_size2d = HUD_Scale(mySize);
  
        teamradar_extraclip_mins = teamradar_extraclip_maxs = '0 0 0'; // we always center
  
        });
        AL_EACH(_entcs, e, it != NULL, {
                if (!it.m_entcs_private) continue;
 -              if (entcs_is_self(it)) continue;
 +              if (it.sv_entnum == current_player) continue;
                color2 = entcs_GetTeam(it.sv_entnum);
                draw_teamradar_player(it.origin, it.angles, Team_ColorRGB(color2));
        });
 -      draw_teamradar_player(view_origin, view_angles, '1 1 1');
 +      draw_teamradar_player(entcs_receiver(current_player).origin, view_angles, '1 1 1');
  
        drawresetcliparea();
  
index ab9c9e24ca0503791ba868edbc3eecbeac40990f,7aac7a39e3d418929620db41e6beab51681b3840..984cb4f50519097481f08ee8d701babcf058e160
@@@ -59,8 -59,6 +59,8 @@@ void HUD_Weapons(
        vector weapon_pos, weapon_size = '0 0 0';
        vector color;
  
 +      entity panel_switchweapon = NULL;
 +
        // check to see if we want to continue
        if(hud != HUD_NORMAL) return;
  
                        FOREACH(Weapons, it != WEP_Null && it.impulse >= 0 && (it.impulse % 3 != 0) && j < 6, {
                                if(!(it.spawnflags & WEP_FLAG_MUTATORBLOCKED))
                                {
 +                                      if(!panel_switchweapon || j < 4)
 +                                              panel_switchweapon = it;
                                        weapons_stat |= it.m_wepset;
                                        ++j;
                                }
        }
  
        // draw the background, then change the virtual size of it to better fit other items inside
+       if (autocvar_hud_panel_weapons_dynamichud)
+               HUD_Scale_Enable();
+       else
+               HUD_Scale_Disable();
        HUD_Panel_DrawBg(1);
  
        if(center.x == -1)
        // draw items
        row = column = 0;
        vector label_size = '1 1 0' * min(weapon_size.x, weapon_size.y) * bound(0, autocvar_hud_panel_weapons_label_scale, 1);
 -      vector noncurrent_pos = '0 0 0';
 -      vector noncurrent_size = weapon_size * bound(0, autocvar_hud_panel_weapons_noncurrent_scale, 1);
 +      vector noncurrent_size = weapon_size * bound(0.01, autocvar_hud_panel_weapons_noncurrent_scale, 1);
        float noncurrent_alpha = panel_fg_alpha * bound(0, autocvar_hud_panel_weapons_noncurrent_alpha, 1);
        bool isCurrent;
 +      static vector weapon_pos_current = '-1 0 0';
 +      if(weapon_pos_current.x == -1)
 +              weapon_pos_current = panel_pos;
 +
 +      float switch_speed;
 +      if(autocvar_hud_panel_weapons_selection_speed <= 0 || autocvar__hud_configure)
 +              switch_speed = 999;
 +      else
 +              switch_speed = frametime * autocvar_hud_panel_weapons_selection_speed;
 +      vector radius_size = weapon_size * (autocvar_hud_panel_weapons_selection_radius + 1);
 +
 +      if(!panel_switchweapon)
 +              panel_switchweapon = switchweapon;
 +
 +      // draw background behind currently selected weapon
 +      // do it earlier to make sure bg is drawn behind every weapon icons while it's moving
 +      if(panel_switchweapon)
 +              drawpic_aspect_skin(weapon_pos_current, "weapon_current_bg", weapon_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
  
        for(i = 0; i <= WEP_LAST-WEP_FIRST; ++i)
        {
  
                // figure out the drawing position of weapon
                weapon_pos = (panel_pos + eX * column * weapon_size.x + eY * row * weapon_size.y);
 -              noncurrent_pos.x = weapon_pos.x + (weapon_size.x - noncurrent_size.x) / 2;
 -              noncurrent_pos.y = weapon_pos.y + (weapon_size.y - noncurrent_size.y) / 2;
  
 -              // draw background behind currently selected weapon
 -              isCurrent = (it == switchweapon);
 +              // update position of the currently selected weapon
 +              isCurrent = (it == panel_switchweapon);
                if(isCurrent)
 -                      drawpic_aspect_skin(weapon_pos, "weapon_current_bg", weapon_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
 +              {
 +                      if(weapon_pos_current.y > weapon_pos.y)
 +                              weapon_pos_current.y = max(weapon_pos.y, weapon_pos_current.y - switch_speed * (weapon_pos_current.y - weapon_pos.y));
 +                      else if(weapon_pos_current.y < weapon_pos.y)
 +                              weapon_pos_current.y = min(weapon_pos.y, weapon_pos_current.y + switch_speed * (weapon_pos.y - weapon_pos_current.y));
 +                      if(weapon_pos_current.x > weapon_pos.x)
 +                              weapon_pos_current.x = max(weapon_pos.x, weapon_pos_current.x - switch_speed * (weapon_pos_current.x - weapon_pos.x));
 +                      else if(weapon_pos_current.x < weapon_pos.x)
 +                              weapon_pos_current.x = min(weapon_pos.x, weapon_pos_current.x + switch_speed * (weapon_pos.x - weapon_pos_current.x));
 +              }
  
                // draw the weapon accuracy
                if(autocvar_hud_panel_weapons_accuracy)
                        }
                }
  
 +              vector weapon_size_real = noncurrent_size;
 +              float weapon_alpha_real = noncurrent_alpha;
 +              float radius_factor_x = 1 - bound(0, fabs(weapon_pos.x - weapon_pos_current.x) / radius_size.x, 1);
 +              float radius_factor_y = 1 - bound(0, fabs(weapon_pos.y - weapon_pos_current.y) / radius_size.y, 1);
 +              if(radius_factor_x || radius_factor_y)
 +              {
 +                      weapon_size_real.x += (weapon_size.x - noncurrent_size.x) * radius_factor_x;
 +                      weapon_size_real.y += (weapon_size.y - noncurrent_size.y) * radius_factor_y;
 +                      weapon_alpha_real += (panel_fg_alpha - noncurrent_alpha) * min(radius_factor_x, radius_factor_y);
 +              }
 +
 +              vector weapon_pos_real = weapon_pos;
 +              weapon_pos_real.x = weapon_pos.x + (weapon_size.x - weapon_size_real.x) / 2;
 +              weapon_pos_real.y = weapon_pos.y + (weapon_size.y - weapon_size_real.y) / 2;
 +
                // drawing all the weapon items
                if(weapons_stat & WepSet_FromWeapon(it))
                {
                        // draw the weapon image
 -                      if(isCurrent)
 -                              drawpic_aspect_skin(weapon_pos, it.model2, weapon_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
 -                      else
 -                              drawpic_aspect_skin(noncurrent_pos, it.model2, noncurrent_size, '1 1 1', noncurrent_alpha, DRAWFLAG_NORMAL);
 +                      drawpic_aspect_skin(weapon_pos_real, it.model2, weapon_size_real, '1 1 1', weapon_alpha_real, DRAWFLAG_NORMAL);
  
                        // draw weapon label string
                        switch(autocvar_hud_panel_weapons_label)
                }
                else // draw a "ghost weapon icon" if you don't have the weapon
                {
 -                      drawpic_aspect_skin(noncurrent_pos, it.model2, noncurrent_size, '0.2 0.2 0.2', panel_fg_alpha * 0.5, DRAWFLAG_NORMAL);
 +                      drawpic_aspect_skin(weapon_pos_real, it.model2, weapon_size_real, '0.2 0.2 0.2', weapon_alpha_real * 0.5, DRAWFLAG_NORMAL);
                }
  
                // draw the complain message
diff --combined qcsrc/client/main.qc
index 39c30f7d528aefe7cfef76adf106a5dc8fb8990c,7caf6905be1d0f47c36271f44e8c3f09d26fc4ba..41ab8839f0eaac2458f4a46ebd2a1162ccf8aacc
@@@ -290,8 -290,9 +290,8 @@@ float SetTeam(entity o, int Team
        return false;
  }
  
 -void Playerchecker_Think()
 +void Playerchecker_Think(entity this)
  {
 -      SELFPARAM();
      int i;
        entity e;
        for(i = 0; i < maxclients; ++i)
@@@ -336,7 -337,7 +336,7 @@@ void TrueAim_Init()
  void PostInit()
  {
        entity playerchecker = new_pure(playerchecker);
 -      playerchecker.think = Playerchecker_Think;
 +      setthink(playerchecker, Playerchecker_Think);
        playerchecker.nextthink = time + 0.2;
  
        TrueAim_Init();
@@@ -378,6 -379,19 +378,6 @@@ float CSQC_InputEvent(int bInputType, f
  // --------------------------------------------------------------------------
  // BEGIN OPTIONAL CSQC FUNCTIONS
  
 -.void(entity) predraw_qc;
 -void PreDraw_self()
 -{
 -      SELFPARAM();
 -      if (this.predraw_qc) this.predraw_qc(this);
 -}
 -
 -void setpredraw(entity this, void(entity) pdfunc)
 -{
 -      this.predraw = PreDraw_self;
 -      this.predraw_qc = pdfunc;
 -}
 -
  void Ent_Remove(entity this);
  
  void Ent_RemovePlayerScore(entity this)
@@@ -523,6 -537,7 +523,7 @@@ NET_HANDLE(ENT_CLIENT_CLIENTDATA, bool 
                // clear race stuff
                race_laptime = 0;
                race_checkpointtime = 0;
+               hud_dynamic_shake_factor = -1;
        }
        if (autocvar_hud_panel_healtharmor_progressbar_gfx)
        {
@@@ -1187,21 -1202,16 +1188,21 @@@ string getcommandkey(string text, strin
        keys = db_get(binddb, command);
        if (keys == "")
        {
 +              bool joy_detected = cvar("joy_detected");
                n = tokenize(findkeysforcommand(command, 0)); // uses '...' strings
                for(j = 0; j < n; ++j)
                {
                        k = stof(argv(j));
                        if(k != -1)
                        {
 -                              if ("" == keys)
 -                                      keys = keynumtostring(k);
 +                              string key = keynumtostring(k);
 +                              if(!joy_detected && substring(key, 0, 3) == "JOY")
 +                                      continue;
 +
 +                              if (keys == "")
 +                                      keys = key;
                                else
 -                                      keys = strcat(keys, ", ", keynumtostring(k));
 +                                      keys = strcat(keys, ", ", key);
  
                                ++l;
                                if (autocvar_hud_showbinds_limit > 0 && autocvar_hud_showbinds_limit <= l)
diff --combined qcsrc/client/view.qc
index ae705793450651075782c73e01b308dba010d898,4ddfee12f96ff7f95a32b90c03fb7cd29167c584..dadb8d41d95378b78c6891caa6aeb2af93551fb6
@@@ -114,17 -114,99 +114,99 @@@ float autocvar_cl_leanmodel_lowpass = 0
        highpass(value.z, frac, ref_store.z, ref_out.z); \
  } MACRO_END
  
- void viewmodel_animate(entity this)
+ void calc_followmodel_ofs(entity view)
  {
-       static float prevtime;
-       float frametime = (time - prevtime);
-       prevtime = time;
+       if(cl_followmodel_time == time)
+               return; // cl_followmodel_ofs already calculated for this frame
  
-       if (autocvar_chase_active) return;
-       if (STAT(HEALTH) <= 0) return;
+       float frac;
+       vector gunorg = '0 0 0';
+       static vector vel_average;
+       static vector gunorg_prev = '0 0 0';
+       static vector gunorg_adjustment_highpass;
+       static vector gunorg_adjustment_lowpass;
+       vector vel;
+       if (autocvar_cl_followmodel_velocity_absolute)
+               vel = view.velocity;
+       else
+       {
+               vector forward = '0 0 0', right = '0 0 0', up = '0 0 0';
+               MAKEVECTORS(makevectors, view_angles, forward, right, up);
+               vel.x = view.velocity * forward;
+               vel.y = view.velocity * right * -1;
+               vel.z = view.velocity * up;
+       }
  
-       entity view = CSQCModel_server2csqc(player_localentnum - 1);
+       vel.x = bound(vel_average.x - autocvar_cl_followmodel_limit, vel.x, vel_average.x + autocvar_cl_followmodel_limit);
+       vel.y = bound(vel_average.y - autocvar_cl_followmodel_limit, vel.y, vel_average.y + autocvar_cl_followmodel_limit);
+       vel.z = bound(vel_average.z - autocvar_cl_followmodel_limit, vel.z, vel_average.z + autocvar_cl_followmodel_limit);
+       frac = avg_factor(autocvar_cl_followmodel_velocity_lowpass);
+       lowpass3(vel, frac, vel_average, gunorg);
+       gunorg *= -autocvar_cl_followmodel_speed * 0.042;
+       // perform highpass/lowpass on the adjustment vectors (turning velocity into acceleration!)
+       // trick: we must do the lowpass LAST, so the lowpass vector IS the final vector!
+       frac = avg_factor(autocvar_cl_followmodel_highpass);
+       highpass3(gunorg, frac, gunorg_adjustment_highpass, gunorg);
+       frac = avg_factor(autocvar_cl_followmodel_lowpass);
+       lowpass3(gunorg, frac, gunorg_adjustment_lowpass, gunorg);
+       if (autocvar_cl_followmodel_velocity_absolute)
+       {
+               vector fixed_gunorg;
+               vector forward = '0 0 0', right = '0 0 0', up = '0 0 0';
+               MAKEVECTORS(makevectors, view_angles, forward, right, up);
+               fixed_gunorg.x = gunorg * forward;
+               fixed_gunorg.y = gunorg * right * -1;
+               fixed_gunorg.z = gunorg * up;
+               gunorg = fixed_gunorg;
+       }
+       cl_followmodel_ofs = gunorg;
+       cl_followmodel_time = time;
+ }
+ vector leanmodel_ofs(entity view)
+ {
+       float frac;
+       vector gunangles = '0 0 0';
+       static vector gunangles_prev = '0 0 0';
+       static vector gunangles_highpass = '0 0 0';
+       static vector gunangles_adjustment_highpass;
+       static vector gunangles_adjustment_lowpass;
+       if (view.csqcmodel_teleported)
+               gunangles_prev = view_angles;
+       // in the highpass, we _store_ the DIFFERENCE to the actual view angles...
+       gunangles_highpass += gunangles_prev;
+       PITCH(gunangles_highpass) += 360 * floor((PITCH(view_angles) - PITCH(gunangles_highpass)) / 360 + 0.5);
+       YAW(gunangles_highpass) += 360 * floor((YAW(view_angles) - YAW(gunangles_highpass)) / 360 + 0.5);
+       ROLL(gunangles_highpass) += 360 * floor((ROLL(view_angles) - ROLL(gunangles_highpass)) / 360 + 0.5);
+       frac = avg_factor(autocvar_cl_leanmodel_highpass1);
+       highpass2_limited(view_angles, frac, autocvar_cl_leanmodel_limit, gunangles_highpass, gunangles);
+       gunangles_prev = view_angles;
+       gunangles_highpass -= gunangles_prev;
  
+       PITCH(gunangles) *= -autocvar_cl_leanmodel_speed;
+       YAW(gunangles) *= -autocvar_cl_leanmodel_speed;
+       // we assume here: PITCH = 0, YAW = 1, ROLL = 2
+       frac = avg_factor(autocvar_cl_leanmodel_highpass);
+       highpass2(gunangles, frac, gunangles_adjustment_highpass, gunangles);
+       frac = avg_factor(autocvar_cl_leanmodel_lowpass);
+       lowpass2(gunangles, frac, gunangles_adjustment_lowpass, gunangles);
+       gunangles.x = -gunangles.x; // pitch was inverted, now that actually matters
+       return gunangles;
+ }
+ vector bobmodel_ofs(entity view)
+ {
        bool clonground = !(view.anim_implicit_state & ANIMIMPLICITSTATE_INAIR);
        static bool oldonground;
        static float hitgroundtime;
        }
        oldonground = clonground;
  
-       bool teleported = view.csqcmodel_teleported;
-       float frac;
-       if(autocvar_cl_followmodel)
+       // calculate for swinging gun model
+       // the gun bobs when running on the ground, but doesn't bob when you're in the air.
+       vector gunorg = '0 0 0';
+       static float bobmodel_scale = 0;
+       static float time_ofs = 0; // makes the effect always restart in the same way
+       if (clonground)
        {
-               vector gunorg = '0 0 0';
-               static vector vel_average;
-               static vector gunorg_prev = '0 0 0';
-               static vector gunorg_adjustment_highpass;
-               static vector gunorg_adjustment_lowpass;
-               vector vel;
-               if(autocvar_cl_followmodel_velocity_absolute)
-                       vel = view.velocity;
-               else
-               {
-                       vector forward = '0 0 0', right = '0 0 0', up = '0 0 0';
-                       MAKEVECTORS(makevectors, view_angles, forward, right, up);
-                       vel.x = view.velocity * forward;
-                       vel.y = view.velocity * right * -1;
-                       vel.z = view.velocity * up;
-               }
-               vel.x = bound(vel_average.x - autocvar_cl_followmodel_limit, vel.x, vel_average.x + autocvar_cl_followmodel_limit);
-               vel.y = bound(vel_average.y - autocvar_cl_followmodel_limit, vel.y, vel_average.y + autocvar_cl_followmodel_limit);
-               vel.z = bound(vel_average.z - autocvar_cl_followmodel_limit, vel.z, vel_average.z + autocvar_cl_followmodel_limit);
-               frac = avg_factor(autocvar_cl_followmodel_velocity_lowpass);
-               lowpass3(vel, frac, vel_average, gunorg);
-               gunorg *= -autocvar_cl_followmodel_speed * 0.042;
-               // perform highpass/lowpass on the adjustment vectors (turning velocity into acceleration!)
-               // trick: we must do the lowpass LAST, so the lowpass vector IS the final vector!
-               frac = avg_factor(autocvar_cl_followmodel_highpass);
-               highpass3(gunorg, frac, gunorg_adjustment_highpass, gunorg);
-               frac = avg_factor(autocvar_cl_followmodel_lowpass);
-               lowpass3(gunorg, frac, gunorg_adjustment_lowpass, gunorg);
-               if(autocvar_cl_followmodel_velocity_absolute)
-               {
-                       vector fixed_gunorg;
-                       vector forward = '0 0 0', right = '0 0 0', up = '0 0 0';
-                       MAKEVECTORS(makevectors, view_angles, forward, right, up);
-                       fixed_gunorg.x = gunorg * forward;
-                       fixed_gunorg.y = gunorg * right * -1;
-                       fixed_gunorg.z = gunorg * up;
-                       gunorg = fixed_gunorg;
-               }
-               this.origin += gunorg;
+               if (time - hitgroundtime > 0.05)
+                       bobmodel_scale = min(1, bobmodel_scale + frametime * 5);
        }
+       else
+               bobmodel_scale = max(0, bobmodel_scale - frametime * 5);
  
-       if(autocvar_cl_leanmodel)
+       float xyspeed = bound(0, vlen(vec2(view.velocity)), 400);
+       if (bobmodel_scale && xyspeed)
        {
-               vector gunangles = '0 0 0';
-               static vector gunangles_prev = '0 0 0';
-               static vector gunangles_highpass = '0 0 0';
-               static vector gunangles_adjustment_highpass;
-               static vector gunangles_adjustment_lowpass;
-               if (teleported)
-                       gunangles_prev = view_angles;
-               // in the highpass, we _store_ the DIFFERENCE to the actual view angles...
-               gunangles_highpass += gunangles_prev;
-               PITCH(gunangles_highpass) += 360 * floor((PITCH(view_angles) - PITCH(gunangles_highpass)) / 360 + 0.5);
-               YAW(gunangles_highpass) += 360 * floor((YAW(view_angles) - YAW(gunangles_highpass)) / 360 + 0.5);
-               ROLL(gunangles_highpass) += 360 * floor((ROLL(view_angles) - ROLL(gunangles_highpass)) / 360 + 0.5);
-               frac = avg_factor(autocvar_cl_leanmodel_highpass1);
-               highpass2_limited(view_angles, frac, autocvar_cl_leanmodel_limit, gunangles_highpass, gunangles);
-               gunangles_prev = view_angles;
-               gunangles_highpass -= gunangles_prev;
+               float bspeed = xyspeed * 0.01 * autocvar_cl_viewmodel_scale * bobmodel_scale;
+               float s = (time - time_ofs) * autocvar_cl_bobmodel_speed;
+               gunorg.y = bspeed * autocvar_cl_bobmodel_side * sin(s);
+               gunorg.z = bspeed * autocvar_cl_bobmodel_up * cos(s * 2);
+       }
+       else
+               time_ofs = time;
  
-               PITCH(gunangles) *= -autocvar_cl_leanmodel_speed;
-               YAW(gunangles) *= -autocvar_cl_leanmodel_speed;
+       return gunorg;
+ }
  
-               // we assume here: PITCH = 0, YAW = 1, ROLL = 2
-               frac = avg_factor(autocvar_cl_leanmodel_highpass);
-               highpass2(gunangles, frac, gunangles_adjustment_highpass, gunangles);
-               frac = avg_factor(autocvar_cl_leanmodel_lowpass);
-               lowpass2(gunangles, frac, gunangles_adjustment_lowpass, gunangles);
+ void viewmodel_animate(entity this)
+ {
+       if (autocvar_chase_active) return;
+       if (STAT(HEALTH) <= 0) return;
+       entity view = CSQCModel_server2csqc(player_localentnum - 1);
  
-               gunangles.x = -gunangles.x; // pitch was inverted, now that actually matters
-               this.angles += gunangles;
+       if (autocvar_cl_followmodel)
+       {
+               calc_followmodel_ofs(view);
+               this.origin += cl_followmodel_ofs;
        }
  
-       float xyspeed = bound(0, vlen(vec2(view.velocity)), 400);
+       if (autocvar_cl_leanmodel)
+               this.angles += leanmodel_ofs(view);
  
        // vertical view bobbing code
        // TODO: cl_bob
  
        // gun model bobbing code
        if (autocvar_cl_bobmodel)
-       {
-               // calculate for swinging gun model
-               // the gun bobs when running on the ground, but doesn't bob when you're in the air.
-               static float bobmodel_scale = 0;
-               static float time_ofs = 0; // makes the effect always restart in the same way
-               if (clonground)
-               {
-                       if (time - hitgroundtime > 0.05)
-                               bobmodel_scale = min(1, bobmodel_scale + frametime * 5);
-               }
-               else
-                       bobmodel_scale = max(0, bobmodel_scale - frametime * 5);
-               if(bobmodel_scale && xyspeed)
-               {
-                       float bspeed = xyspeed * 0.01 * autocvar_cl_viewmodel_scale * bobmodel_scale;
-                       float s = (time - time_ofs) * autocvar_cl_bobmodel_speed;
-                       vector gunorg = '0 0 0';
-                       gunorg.y = bspeed * autocvar_cl_bobmodel_side * sin(s);
-                       gunorg.z = bspeed * autocvar_cl_bobmodel_up * cos(s * 2);
-                       this.origin += gunorg;
-               }
-               else
-                       time_ofs = time;
-       }
+               this.origin += bobmodel_ofs(view);
  }
  
  .vector viewmodel_origin, viewmodel_angles;
  
  void viewmodel_draw(entity this)
  {
-       if(!activeweapon)
+       if(!activeweapon || !autocvar_r_drawviewmodel)
                return;
        int mask = (intermission || (STAT(HEALTH) <= 0) || autocvar_chase_active) ? 0 : MASK_NORMAL;
        float a = this.alpha;
@@@ -741,6 -752,8 +752,6 @@@ bool WantEventchase(entity this
                        return true;
                if(MUTATOR_CALLHOOK(WantEventchase, this))
                        return true;
 -              if(autocvar_cl_eventchase_nexball && gametype == MAPINFO_TYPE_NEXBALL && !(WepSet_GetFromStat() & WEPSET(NEXBALL)))
 -                      return true;
                if(autocvar_cl_eventchase_death && (STAT(HEALTH) <= 0))
                {
                        if(autocvar_cl_eventchase_death == 2)
@@@ -920,8 -933,9 +931,8 @@@ LABEL(normalcolor
        return wcross_color;
  }
  
 -void HUD_Crosshair()
 -{SELFPARAM();
 -      entity e = this;
 +void HUD_Crosshair(entity this)
 +{
        float f, i, j;
        vector v;
        if(!scoreboard_active && !camera_active && intermission != 2 &&
                string wcross_name = "";
                float wcross_scale, wcross_blur;
  
 +        entity e = this; // wtf?
                if(autocvar_crosshair_per_weapon || (autocvar_crosshair_color_special == 1))
                {
                        e = switchingweapon;
        }
  }
  
 -void HUD_Draw()
 +void HUD_Draw(entity this)
  {
        if(!intermission)
        if (MUTATOR_CALLHOOK(HUD_Draw_overlay))
  
                        HUD_Main();
                        HUD_DrawScoreboard();
+                       HUD_Scale_Disable();
                }
  
        // crosshair goes VERY LAST
        UpdateDamage();
 -      HUD_Crosshair();
 +      HUD_Crosshair(this);
        HitSound();
  }
  
@@@ -1358,8 -1372,7 +1370,8 @@@ int lasthud
  float vh_notice_time;
  void WaypointSprite_Load();
  void CSQC_UpdateView(float w, float h)
 -{SELFPARAM();
 +{
 +    SELFPARAM();
      TC(int, w); TC(int, h);
        entity e;
        float fov;
  
        lasthud = hud;
  
+       HUD_Scale_Disable();
        if(autocvar__hud_showbinds_reload) // menu can set this one
        {
                db_close(binddb);
                view_quality = 1;
  
        // this needs to be updated manually now due to the destruction of engine physics stats
 -      if(autocvar_slowmo != STAT(MOVEVARS_TIMESCALE))
 +      if(!isdemo() && autocvar_slowmo != STAT(MOVEVARS_TIMESCALE))
                cvar_set("slowmo", ftos(STAT(MOVEVARS_TIMESCALE)));
  
        button_attack2 = PHYS_INPUT_BUTTON_ATCK2(this);
  
        scoreboard_active = HUD_WouldDrawScoreboard();
  
 -      HUD_Draw();
 +      HUD_Draw(this);
  
        if(NextFrameCommand)
        {