From c0bd852198a68445bbc859d89b81a2aeb083c470 Mon Sep 17 00:00:00 2001 From: MotoLegacy Date: Sat, 7 Dec 2024 01:46:07 -0800 Subject: [PATCH] CLIENT: Add Chasecam from Vril --- progs/csqc.src | 1 + source/client/chasecam.qc | 159 ++++++++++++++++++++++++++++++++++++ source/client/main.qc | 4 + source/client/particles.qc | 2 +- source/client/view_model.qc | 2 +- 5 files changed, 166 insertions(+), 2 deletions(-) create mode 100644 source/client/chasecam.qc diff --git a/progs/csqc.src b/progs/csqc.src index fae4885..c32610e 100644 --- a/progs/csqc.src +++ b/progs/csqc.src @@ -31,5 +31,6 @@ chat.qc user_input.qc view_model.qc particles.qc +chasecam.qc main.qc #endlist \ No newline at end of file diff --git a/source/client/chasecam.qc b/source/client/chasecam.qc new file mode 100644 index 0000000..d2ea19f --- /dev/null +++ b/source/client/chasecam.qc @@ -0,0 +1,159 @@ +/* + client/chasecam.qc + + A pretty direct port of Quake's chase cam to CSQC. + + Copyright (C) 1996-1997 Id Software, Inc. + Copyright (C) 2021-2024 NZ:P Team + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ + +vector chase_pos; +vector chase_angles; + +vector chase_dest; +vector chase_dest_angles; + +void() Chase_Init = +{ + autocvar(chase_back, 100); + autocvar(chase_up, 16); + autocvar(chase_right, 0); + autocvar(chase_active, 0); + autocvar(chase_roll, 0); + autocvar(chase_yaw, 0); + autocvar(chase_pitch, 0); +}; + +float chase_nodraw; + +#define NUM_TESTS 64 +#define CHASE_DEST_OFFSET 2.0 + +void() Chase_Update = +{ + int i; + float dist; + vector cl_viewangles, cl_vieworigin; + vector dest, stop; + int best; + int viewcontents; + + cl_viewangles = getproperty(VF_ANGLES); + cl_vieworigin = getproperty(VF_ORIGIN); + + // if can't see player, reset + makevectors(cl_viewangles); + + // calc exact destination + for (i = 0; i < 3; i++) { + chase_dest[i] = cl_vieworigin[i] - v_forward[i] * autocvar_chase_back - v_right[i] * autocvar_chase_right; + } + + chase_dest[2] = cl_vieworigin[2] + autocvar_chase_up; + + // take contents of the view leaf + viewcontents = pointcontents(cl_vieworigin); + + for (best = 0; best < NUM_TESTS; best++) { + vector chase_newdest; + + chase_newdest[0] = cl_vieworigin[0] + (chase_dest[0] - cl_vieworigin[0]) * best / NUM_TESTS; + chase_newdest[1] = cl_vieworigin[1] + (chase_dest[1] - cl_vieworigin[1]) * best / NUM_TESTS; + chase_newdest[2] = cl_vieworigin[2] + (chase_dest[2] - cl_vieworigin[2]) * best / NUM_TESTS; + + // check for a leaf hit with different contents + if (pointcontents(chase_newdest) != viewcontents) + { + // go back to the previous best as this one is bad + // unless the first one was also bad, (viewleaf contents != viewleaf contents!!!) + if (best > 0) { + best--; + } else { + best = NUM_TESTS; + break; + } + } + } + + // certain surfaces can be viewed at an oblique enough angle that they are partially clipped + // by znear, so now we fix that too... + for (; best >= 0; best--) { + // number of matches + int nummatches = 0; + + // adjust + chase_dest[0] = cl_vieworigin[0] + (chase_dest[0] - cl_vieworigin[0]) * best / NUM_TESTS; + chase_dest[1] = cl_vieworigin[1] + (chase_dest[1] - cl_vieworigin[1]) * best / NUM_TESTS; + chase_dest[2] = cl_vieworigin[2] + (chase_dest[2] - cl_vieworigin[2]) * best / NUM_TESTS; + + // move x to neg + chase_dest[0] -= CHASE_DEST_OFFSET; + if (pointcontents(chase_dest) == viewcontents) nummatches++; + chase_dest[0] += CHASE_DEST_OFFSET; + + // move x to pos + chase_dest[0] += CHASE_DEST_OFFSET; + if (pointcontents(chase_dest) == viewcontents) nummatches++; + chase_dest[0] -= CHASE_DEST_OFFSET; + + // move y to neg + chase_dest[1] -= CHASE_DEST_OFFSET; + if (pointcontents(chase_dest) == viewcontents) nummatches++; + chase_dest[1] += CHASE_DEST_OFFSET; + + // move y to pos + chase_dest[1] += CHASE_DEST_OFFSET; + if (pointcontents(chase_dest) == viewcontents) nummatches++; + chase_dest[1] -= CHASE_DEST_OFFSET; + + // move z to neg + chase_dest[2] -= CHASE_DEST_OFFSET; + if (pointcontents(chase_dest) == viewcontents) nummatches++; + chase_dest[2] += CHASE_DEST_OFFSET; + + // move z to pos + chase_dest[2] += CHASE_DEST_OFFSET; + if (pointcontents(chase_dest) == viewcontents) nummatches++; + chase_dest[2] -= CHASE_DEST_OFFSET; + + // all tests passed so we're good! + if (nummatches == 6) break; + } + + // find the spot the player is looking at + dest[0] = cl_vieworigin[0] + 4096 * v_forward[0]; + dest[1] = cl_vieworigin[1] + 4096 * v_forward[1]; + dest[2] = cl_vieworigin[2] + 4096 * v_forward[2]; + traceline(cl_vieworigin, dest, MOVE_NOMONSTERS, world); + + // calculate pitch to look at the same spot from camera + stop[0] = cl_vieworigin[0] - trace_endpos[0]; + stop[1] = cl_vieworigin[1] - trace_endpos[1]; + stop[2] = cl_vieworigin[2] - trace_endpos[2]; + dist = dotproduct(stop, v_forward); + if (dist < 1) dist = 1; + + cl_viewangles[0] = -atan(stop[2] / dist) / M_PI * 180; + + setviewprop(VF_ORIGIN, chase_dest); + //setviewprop(VF_ANGLES, cl_viewangles); +}; \ No newline at end of file diff --git a/source/client/main.qc b/source/client/main.qc index bc98ec6..cee2149 100644 --- a/source/client/main.qc +++ b/source/client/main.qc @@ -788,6 +788,10 @@ noref void(float width, float height, float menushown) CSQC_UpdateView = setviewprop(VF_ANGLES, camang); + if (cvar("chase_active")) { + Chase_Update(); + } + //does what you think it does renderscene(); diff --git a/source/client/particles.qc b/source/client/particles.qc index b7a01b2..e8b18c9 100644 --- a/source/client/particles.qc +++ b/source/client/particles.qc @@ -38,7 +38,7 @@ vector particle_position; void() Particles_MuzzleflashCallback = { // Do not display Muzzleflashes if we're using a Sniper Scope or not drawing the viewmodel. - if (getstatf(STAT_WEAPONZOOM) == 2 || !cvar("r_drawviewmodel")) + if (getstatf(STAT_WEAPONZOOM) == 2 || !cvar("r_drawviewmodel") || cvar("chase_active")) return; // Organized storage of optional_field and optional_entity. diff --git a/source/client/view_model.qc b/source/client/view_model.qc index b2018d0..16da178 100644 --- a/source/client/view_model.qc +++ b/source/client/view_model.qc @@ -276,7 +276,7 @@ void() ViewModel_Draw = } // If we're zoomed in with a Sniper scope or r_drawviewmodel is false, early-out. - if (!cvar("r_drawviewmodel") || getstatf(STAT_WEAPONZOOM) == 2) + if (!cvar("r_drawviewmodel") || getstatf(STAT_WEAPONZOOM) == 2 || cvar("chase_active")) return; cl_viewent.origin = cl_viewent2.origin = '0 0 0';