]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blobdiff - qcsrc/client/view.qc
Enforce first person view while in vehicle gunner slots
[xonotic/xonotic-data.pk3dir.git] / qcsrc / client / view.qc
index 71dd6127934c778f4e77b127ec821a7debaaa4d3..a992d36a4858a114a8934df8dad348c275095726 100644 (file)
@@ -48,6 +48,7 @@
 #define EFMASK_CHEAP (EF_ADDITIVE | EF_DOUBLESIDED | EF_FULLBRIGHT | EF_NODEPTHTEST | EF_NODRAW | EF_NOSHADOW | EF_SELECTABLE | EF_TELEPORT_BIT)
 
 float autocvar_cl_viewmodel_scale;
+float autocvar_cl_viewmodel_alpha;
 
 bool autocvar_cl_bobmodel;
 float autocvar_cl_bobmodel_speed;
@@ -75,54 +76,46 @@ float autocvar_cl_leanmodel_lowpass = 0.05;
        ret = ref_store = ref_store * (1 - frac) + (value) * frac;
 
 #define lowpass_limited(value, frac, limit, ref_store, ret) MACRO_BEGIN \
-{ \
        float __ignore; lowpass(value, frac, ref_store, __ignore); \
        ret = ref_store = bound((value) - (limit), ref_store, (value) + (limit)); \
-MACRO_END
+MACRO_END
 
 #define highpass(value, frac, ref_store, ret) MACRO_BEGIN \
-{ \
        float __f = 0; lowpass(value, frac, ref_store, __f); \
        ret = (value) - __f; \
-MACRO_END
+MACRO_END
 
 #define highpass_limited(value, frac, limit, ref_store, ret) MACRO_BEGIN \
-{ \
        float __f = 0; lowpass_limited(value, frac, limit, ref_store, __f); \
        ret = (value) - __f; \
-MACRO_END
+MACRO_END
 
 #define lowpass2(value, frac, ref_store, ref_out) MACRO_BEGIN \
-{ \
        lowpass(value.x, frac, ref_store.x, ref_out.x); \
        lowpass(value.y, frac, ref_store.y, ref_out.y); \
-MACRO_END
+MACRO_END
 
 #define highpass2(value, frac, ref_store, ref_out) MACRO_BEGIN \
-{ \
        highpass(value.x, frac, ref_store.x, ref_out.x); \
        highpass(value.y, frac, ref_store.y, ref_out.y); \
-MACRO_END
+MACRO_END
 
 #define highpass2_limited(value, frac, limit, ref_store, ref_out) MACRO_BEGIN \
-{ \
        highpass_limited(value.x, frac, limit, ref_store.x, ref_out.x); \
        highpass_limited(value.y, frac, limit, ref_store.y, ref_out.y); \
-MACRO_END
+MACRO_END
 
 #define lowpass3(value, frac, ref_store, ref_out) MACRO_BEGIN \
-{ \
        lowpass(value.x, frac, ref_store.x, ref_out.x); \
        lowpass(value.y, frac, ref_store.y, ref_out.y); \
        lowpass(value.z, frac, ref_store.z, ref_out.z); \
-MACRO_END
+MACRO_END
 
 #define highpass3(value, frac, ref_store, ref_out) MACRO_BEGIN \
-{ \
        highpass(value.x, frac, ref_store.x, ref_out.x); \
        highpass(value.y, frac, ref_store.y, ref_out.y); \
        highpass(value.z, frac, ref_store.z, ref_out.z); \
-MACRO_END
+MACRO_END
 
 void calc_followmodel_ofs(entity view)
 {
@@ -297,12 +290,10 @@ void viewmodel_draw(entity this)
        if(!this.activeweapon || !autocvar_r_drawviewmodel)
                return;
        int mask = (intermission || (STAT(HEALTH) <= 0) || autocvar_chase_active) ? 0 : MASK_NORMAL;
-       float a = this.alpha;
-       static bool wasinvehicle;
+       float a = ((autocvar_cl_viewmodel_alpha) ? bound(-1, autocvar_cl_viewmodel_alpha, this.m_alpha) : this.m_alpha);
+       int wepskin = this.m_skin;
        bool invehicle = player_localentnum > maxclients;
        if (invehicle) a = -1;
-       else if (wasinvehicle) a = 1;
-       wasinvehicle = invehicle;
        Weapon wep = this.activeweapon;
        int c = entcs_GetClientColors(current_player);
        vector g = weaponentity_glowmod(wep, NULL, c, this);
@@ -314,6 +305,7 @@ void viewmodel_draw(entity this)
        {
                e.drawmask = mask;
                e.alpha = a;
+               e.skin = wepskin;
                e.colormap = 256 + c;  // colormap == 0 is black, c == 0 is white
                e.glowmod = g;
                e.csqcmodel_effects = fx;
@@ -402,82 +394,6 @@ STATIC_INIT(fpscounter_init)
        showfps_prevfps_time = currentTime; // we must initialize it to avoid an instant low frame sending
 }
 
-STATIC_INIT(Porto)
-{
-       entity e = new_pure(porto);
-       e.draw = Porto_Draw;
-       IL_PUSH(g_drawables, e);
-       e.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_PLAYERCLIP;
-}
-
-const int polyline_length = 16;
-.vector polyline[polyline_length];
-void Porto_Draw(entity this)
-{
-       for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
-       {
-               entity wepent = viewmodels[slot];
-
-               if (wepent.activeweapon != WEP_PORTO) continue;
-               if (spectatee_status) continue;
-               if (WEP_CVAR(porto, secondary)) continue;
-               if (intermission == 1) continue;
-               if (intermission == 2) continue;
-               if (STAT(HEALTH) <= 0) continue;
-
-               vector pos = view_origin;
-               vector dir = view_forward;
-               if (wepent.angles_held_status)
-               {
-                       makevectors(wepent.angles_held);
-                       dir = v_forward;
-               }
-
-               wepent.polyline[0] = pos;
-
-               int portal_number = 0, portal1_idx = 1, portal_max = 2;
-               int n = 1 + 2;  // 2 lines == 3 points
-               for (int idx = 0; idx < n && idx < polyline_length - 1; )
-               {
-                       traceline(pos, pos + 65536 * dir, true, this);
-                       dir = reflect(dir, trace_plane_normal);
-                       pos = trace_endpos;
-                       wepent.polyline[++idx] = pos;
-                       if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SLICK || trace_dphitcontents & DPCONTENTS_PLAYERCLIP)
-                       {
-                               n += 1;
-                               continue;
-                       }
-                       if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
-                       {
-                               n = max(2, idx);
-                               break;
-                       }
-                       // check size
-                       {
-                               vector ang = vectoangles2(trace_plane_normal, dir);
-                               ang.x = -ang.x;
-                               makevectors(ang);
-                               if (!CheckWireframeBox(this, pos - 48 * v_right - 48 * v_up + 16 * v_forward, 96 * v_right, 96 * v_up, 96 * v_forward))
-                               {
-                                       n = max(2, idx);
-                                       break;
-                               }
-                       }
-                       portal_number += 1;
-                       if (portal_number >= portal_max) break;
-                       if (portal_number == 1) portal1_idx = idx;
-               }
-               for (int idx = 0; idx < n - 1; ++idx)
-               {
-                       vector p = wepent.polyline[idx], q = wepent.polyline[idx + 1];
-                       if (idx == 0) p -= view_up * 16;  // line from player
-                       vector rgb = (idx < portal1_idx) ? '1 0 0' : '0 0 1';
-                       Draw_CylindricLine(p, q, 4, "", 1, 0, rgb, 0.5, DRAWFLAG_NORMAL, view_origin);
-               }
-       }
-}
-
 float drawtime;
 float avgspeed;
 vector GetCurrentFov(float fov)
@@ -491,9 +407,8 @@ vector GetCurrentFov(float fov)
        if(zoomfactor < 1 || zoomfactor > 30)
                zoomfactor = 2.5;
        zoomspeed = autocvar_cl_zoomspeed;
-       if(zoomspeed >= 0)
-       if(zoomspeed < 0.5 || zoomspeed > 16)
-                       zoomspeed = 3.5;
+       if (zoomspeed >= 0 && (zoomspeed < 0.5 || zoomspeed > 16))
+               zoomspeed = 3.5;
 
        zoomdir = button_zoom;
 
@@ -526,7 +441,11 @@ vector GetCurrentFov(float fov)
 
        if(zoomdir) { zoomin_effect = 0; }
 
-       if(camera_active)
+       if (spectatee_status > 0 && STAT(CAMERA_SPECTATOR) == 2)
+       {
+               current_viewzoom = 1;
+       }
+       else if (camera_active)
        {
                current_viewzoom = min(1, current_viewzoom + drawframetime);
        }
@@ -570,10 +489,10 @@ vector GetCurrentFov(float fov)
 
        if(autocvar_cl_velocityzoom_enabled && autocvar_cl_velocityzoom_type) // _type = 0 disables velocity zoom too
        {
-               if(intermission) { curspeed = 0; }
+               if (intermission || (spectatee_status > 0 && STAT(CAMERA_SPECTATOR) == 2))
+                       curspeed = 0;
                else
                {
-
                        makevectors(view_angles);
                        v = pmove_vel;
                        if(csqcplayer)
@@ -628,6 +547,8 @@ vector GetOrthoviewFOV(vector ov_worldmin, vector ov_worldmax, vector ov_mid, ve
 // this function must match W_SetupShot!
 float zoomscript_caught;
 
+bool minigame_wasactive;
+
 vector wcross_origin;
 float wcross_scale_prev, wcross_alpha_prev;
 vector wcross_color_prev;
@@ -680,6 +601,9 @@ float EnemyHitCheck()
 
 float TrueAimCheck(entity wepent)
 {
+       if(wepent.activeweapon.spawnflags & WEP_FLAG_NOTRUEAIM)
+               return SHOTTYPE_HITWORLD;
+
        float nudge = 1; // added to traceline target and subtracted from result TOOD(divVerent): do we still need this? Doesn't the engine do this now for us?
        vector vecs, trueaimpoint, w_shotorg;
        vector mi, ma, dv;
@@ -693,12 +617,6 @@ float TrueAimCheck(entity wepent)
 
        switch(wepent.activeweapon) // WEAPONTODO
        {
-               case WEP_TUBA: // no aim
-               case WEP_PORTO: // shoots from eye
-               case WEP_NEXBALL: // shoots from eye
-               case WEP_HOOK: // no trueaim
-               case WEP_MORTAR: // toss curve
-                       return SHOTTYPE_HITWORLD;
                case WEP_VORTEX:
                case WEP_OVERKILL_NEX:
                case WEP_VAPORIZER:
@@ -796,34 +714,41 @@ vector liquidcolor_prev;
 
 float eventchase_current_distance;
 float eventchase_running;
-bool WantEventchase(entity this)
+int WantEventchase(entity this, bool want_vehiclechase)
 {
        if(autocvar_cl_orthoview)
-               return false;
+               return 0;
        if(STAT(GAME_STOPPED) || intermission)
-               return true;
+               return 1;
        if(this.viewloc)
-               return true;
+               return 1;
        if(spectatee_status >= 0)
        {
-               if(hud != HUD_NORMAL && (autocvar_cl_eventchase_vehicle || spectatee_status > 0))
-                       return true;
+               if(want_vehiclechase)
+                       return 1;
                if(MUTATOR_CALLHOOK(WantEventchase, this))
-                       return true;
+                       return 1;
                if(autocvar_cl_eventchase_frozen && STAT(FROZEN))
-                       return true;
+                       return 1;
                if(autocvar_cl_eventchase_death && (STAT(HEALTH) <= 0))
                {
                        if(autocvar_cl_eventchase_death == 2)
                        {
                                // don't stop eventchase once it's started (even if velocity changes afterwards)
                                if(this.velocity == '0 0 0' || eventchase_running)
-                                       return true;
+                                       return 1;
                        }
-                       else return true;
+                       else return 1;
+               }
+               if (spectatee_status > 0 && autocvar_cl_eventchase_spectated_change)
+               {
+                       if (time <= spectatee_status_changed_time + min(3, autocvar_cl_eventchase_spectated_change_time))
+                               return 1;
+                       else if (eventchase_running)
+                               return -1; // disable chase_active while eventchase is still enabled so to avoid a glicth
                }
        }
-       return false;
+       return 0;
 }
 
 void HUD_Crosshair_Vehicle(entity this)
@@ -1049,6 +974,20 @@ void HUD_Crosshair(entity this)
                // wcross_origin = '0.5 0 0' * vid_conwidth + '0 0.5 0' * vid_conheight;
                if(csqcplayer.viewloc && (csqcplayer.viewloc.spawnflags & VIEWLOC_FREEAIM))
                        wcross_origin = viewloc_mousepos;
+               else if(autocvar_chase_active > 0 && autocvar_crosshair_chase)
+               {
+                       vector player_org = ((csqcplayer) ? csqcplayer.origin + csqcplayer.view_ofs : view_origin);
+                       if(csqcplayer && crosshair_chase_playeralpha && crosshair_chase_playeralpha < 1)
+                       {
+                               traceline(view_origin, view_origin + max_shot_distance * view_forward, MOVE_NORMAL, NULL);
+                               if(trace_ent == csqcplayer && STAT(HEALTH) > 0)
+                                       csqcplayer.alpha = crosshair_chase_playeralpha;
+                               else
+                                       csqcplayer.alpha = csqcplayer.m_alpha;
+                       }
+                       traceline(player_org, player_org + max_shot_distance * view_forward, MOVE_WORLDONLY, NULL);
+                       wcross_origin = project_3d_to_2d(trace_endpos);
+               }
                else
                        wcross_origin = project_3d_to_2d(view_origin + max_shot_distance * view_forward);
                wcross_origin.z = 0;
@@ -1342,21 +1281,22 @@ void HUD_Crosshair(entity this)
                        }
 
 #define CROSSHAIR_DO_BLUR(M,sz,wcross_name,wcross_alpha) \
-                       MACRO_BEGIN { \
+                       MACRO_BEGIN \
+                               vector scaled_sz = sz * wcross_size; \
                                if(wcross_blur > 0) \
                                { \
                                        for(i = -2; i <= 2; ++i) \
                                        for(j = -2; j <= 2; ++j) \
-                                       M(i,j,sz,wcross_name,wcross_alpha*0.04); \
+                                       M(i,j,sz,scaled_sz,wcross_name,wcross_alpha*0.04); \
                                } \
                                else \
                                { \
-                                       M(0,0,sz,wcross_name,wcross_alpha); \
+                                       M(0,0,sz,scaled_sz,wcross_name,wcross_alpha); \
                                } \
-                       MACRO_END
+                       MACRO_END
 
-#define CROSSHAIR_DRAW_SINGLE(i,j,sz,wcross_name,wcross_alpha) \
-                       drawpic(wcross_origin - ('0.5 0 0' * (sz * wcross_size.x + i * wcross_blur) + '0 0.5 0' * (sz * wcross_size.y + j * wcross_blur)), wcross_name, sz * wcross_size, wcross_color, wcross_alpha, DRAWFLAG_NORMAL)
+#define CROSSHAIR_DRAW_SINGLE(i,j,sz,scaled_sz,wcross_name,wcross_alpha) \
+                       drawpic(wcross_origin - ('0.5 0 0' * (scaled_sz.x + i * wcross_blur) + '0 0.5 0' * (scaled_sz.y + j * wcross_blur)), wcross_name, scaled_sz, wcross_color, wcross_alpha, DRAWFLAG_NORMAL)
 
 #define CROSSHAIR_DRAW(sz,wcross_name,wcross_alpha) \
                        CROSSHAIR_DO_BLUR(CROSSHAIR_DRAW_SINGLE,sz,wcross_name,wcross_alpha)
@@ -1525,7 +1465,7 @@ void HUD_Draw(entity this)
        if(autocvar_r_letterbox == 0)
                if(autocvar_viewsize < 120)
                {
-                       if(!(gametype == MAPINFO_TYPE_RACE || gametype == MAPINFO_TYPE_CTS))
+                       if(!(ISGAMETYPE(RACE) || ISGAMETYPE(CTS)))
                                Accuracy_LoadLevels();
 
                        HUD_Main();
@@ -1552,17 +1492,16 @@ void ViewLocation_Mouse()
        //draw_cursor(viewloc_mousepos, '0.5 0.5 0', "/cursor_move", '1 1 1', cursor_alpha);
 }
 
-float mouse_over_panel;
-void HUD_Draw_Mouse()
+void HUD_Cursor_Show()
 {
        float cursor_alpha = 1 - autocvar__menu_alpha;
-       if(!mouse_over_panel)
+       if(cursor_type == CURSOR_NORMAL)
                draw_cursor_normal(mousepos, '1 1 1', cursor_alpha);
-       else if(mouse_over_panel == 1)
+       else if(cursor_type == CURSOR_MOVE)
                draw_cursor(mousepos, '0.5 0.5 0', "/cursor_move", '1 1 1', cursor_alpha);
-       else if(mouse_over_panel == 2)
+       else if(cursor_type == CURSOR_RESIZE)
                draw_cursor(mousepos, '0.5 0.5 0', "/cursor_resize", '1 1 1', cursor_alpha);
-       else
+       else if(cursor_type == CURSOR_RESIZE2)
                draw_cursor(mousepos, '0.5 0.5 0', "/cursor_resize2", '1 1 1', cursor_alpha);
 }
 
@@ -1571,28 +1510,40 @@ void HUD_Mouse(entity player)
        if(autocvar__menu_alpha == 1)
                return;
 
-       if(!HUD_WouldShowCursor())
+       if(!cursor_active)
        {
                if(player.viewloc && (player.viewloc.spawnflags & VIEWLOC_FREEAIM))
                        ViewLocation_Mouse(); // NOTE: doesn't use cursormode
                return;
        }
 
+       if (cursor_active == -1) // starting to display the cursor
+       {
+               // since HUD_Mouse is called by CSQC_UpdateView before CSQC_InputEvent,
+               // in the first frame mousepos is the mouse position of the last time
+               // the cursor was displayed, thus we ignore it to avoid a glictch
+               cursor_active = 1;
+               return;
+       }
+
        if(!autocvar_hud_cursormode)
                update_mousepos();
 
        if(autocvar__hud_configure)
                HUD_Panel_Mouse();
-       if(HUD_MinigameMenu_IsOpened() || active_minigame)
-               HUD_Minigame_Mouse();
-       if(QuickMenu_IsOpened())
-               QuickMenu_Mouse();
-       if(HUD_Radar_Clickable())
-               HUD_Radar_Mouse();
+       else
+       {
+               if (HUD_MinigameMenu_IsOpened())
+                       HUD_Minigame_Mouse();
+               if (QuickMenu_IsOpened())
+                       QuickMenu_Mouse();
+               if (HUD_Radar_Clickable())
+                       HUD_Radar_Mouse();
+       }
 
        prevMouseClicked = mouseClicked;
 
-       HUD_Draw_Mouse();
+       HUD_Cursor_Show();
 }
 
 bool ov_enabled;
@@ -1608,7 +1559,7 @@ int lasthud;
 float vh_notice_time;
 void CSQC_UpdateView(entity this, float w, float h)
 {
-    TC(int, w); TC(int, h);
+       TC(int, w); TC(int, h);
        entity e;
        float fov;
        float f;
@@ -1627,6 +1578,10 @@ void CSQC_UpdateView(entity this, float w, float h)
 
        lasthud = hud;
 
+       ReplicateVars(false);
+       if (ReplicateVars_NOT_SENDING())
+               ReplicateVars_DELAY(0.8 + random() * 0.4); // no need to check cvars every frame
+
        HUD_Scale_Disable();
 
        if(autocvar__hud_showbinds_reload) // menu can set this one
@@ -1716,7 +1671,7 @@ void CSQC_UpdateView(entity this, float w, float h)
                else if(autocvar_chase_active == -2)
                        cvar_set("chase_active", "0");
 
-               float vehicle_chase = (hud != HUD_NORMAL && (autocvar_cl_eventchase_vehicle || spectatee_status > 0));
+               bool vehicle_chase = (hud != HUD_NORMAL && (autocvar_cl_eventchase_vehicle || spectatee_status > 0));
 
                float vehicle_viewdist = 0;
                vector vehicle_viewofs = '0 0 0';
@@ -1728,10 +1683,15 @@ void CSQC_UpdateView(entity this, float w, float h)
                                Vehicle info = Vehicles_from(hud);
                                vehicle_viewdist = info.height;
                                vehicle_viewofs = info.view_ofs;
+                               if(vehicle_viewdist < 0) // when set below 0, this vehicle doesn't use third person view (gunner slots)
+                                       vehicle_chase = false;
                        }
+                       else
+                               vehicle_chase = false;
                }
 
-               if(WantEventchase(this))
+               int eventchase = WantEventchase(this, vehicle_chase);
+               if (eventchase)
                {
                        vector current_view_origin_override = '0 0 0';
                        vector view_offset_override = '0 0 0';
@@ -1809,7 +1769,8 @@ void CSQC_UpdateView(entity this, float w, float h)
                        if(!local_player.viewloc)
                                setproperty(VF_ANGLES, WarpZone_TransformVAngles(WarpZone_trace_transform, view_angles));
                }
-               else if(autocvar_chase_active < 0) // time to disable chase_active if it was set by this code
+
+               if (eventchase <= 0 && autocvar_chase_active < 0) // time to disable chase_active if it was set by this code
                {
                        eventchase_running = false;
                        cvar_set("chase_active", "0");
@@ -1928,10 +1889,7 @@ void CSQC_UpdateView(entity this, float w, float h)
        // Render the Scene
        view_origin = getpropertyvec(VF_ORIGIN);
        view_angles = getpropertyvec(VF_ANGLES);
-       makevectors(view_angles);
-       view_forward = v_forward;
-       view_right = v_right;
-       view_up = v_up;
+       MAKEVECTORS(makevectors, view_angles, view_forward, view_right, view_up);
 
 #ifdef BLURTEST
        if(time > blurtest_time0 && time < blurtest_time1)
@@ -2000,6 +1958,20 @@ void CSQC_UpdateView(entity this, float w, float h)
                }
        }
 
+       if(active_minigame && HUD_MinigameMenu_IsOpened())
+       {
+               if(!minigame_wasactive)
+               {
+                       localcmd("+button12\n");
+                       minigame_wasactive = true;
+               }
+       }
+       else if(minigame_wasactive)
+       {
+               localcmd("-button12\n");
+               minigame_wasactive = false;
+       }
+
        ColorTranslateMode = autocvar_cl_stripcolorcodes;
 
        for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
@@ -2157,7 +2129,7 @@ void CSQC_UpdateView(entity this, float w, float h)
                R_EndPolygon();
        }
 
-       if(autocvar_cl_reticle)
+       if(autocvar_cl_reticle && !MUTATOR_CALLHOOK(DrawReticle))
        {
                string reticle_image = string_null;
                bool wep_zoomed = false;
@@ -2435,7 +2407,7 @@ void CSQC_UpdateView(entity this, float w, float h)
        else if(cvar("r_glsl_postprocess") == 2)
                cvar_set("r_glsl_postprocess", "0");
 
-       /*if(gametype == MAPINFO_TYPE_CTF)
+       /*if(ISGAMETYPE(CTF))
          {
          ctf_view();
          } else */