Name:                   Putting a Chase Cam in your QuakeWorld client.
Author:                 Rich Whitehouse
E-mail:                 thefatal@telefragged.com
Homepage:               http://www.telefragged.com/thefatal/

COPYRIGHTS/PERMISSIONS:
This source code cannot be used in anyway without giving me (the
author) full credit for its use. It can be distributed freely as long
as the distribution includes this text file. This source code is
distributed under the GPL (gnu public license, see gnu.txt for details)
just as the original Quake source code.
----------------------------------------------------------------------
This tutorial will show you how to put a real functional chasecam into your
QuakeWorld client, which can be used on any server in any mod.

First you'll need the Quake source code. This tutorial tells you what to do
for the QuakeWorld client code, I'm not sure if it'd work the same for Quake
or not. Just do what it says to in the appropriate files and you'll be all
set.

CL_ENTS.C
---------
In void CL_LinkPlayers (void), find this:

	if (j == cl.playernum)
		continue;

Change it to this:

	if (j == cl.playernum && !cl_chase.value)
		continue;

Now find this:

		CL_SetSolidPlayers (j);
		CL_PredictUsercmd (state, &exact, &state->command, false);
		pmove.numphysent = oldphysent;
		VectorCopy (exact.origin, ent->origin);
	}

Directly under it, add this:

	//RICH
	if (j == cl.playernum) {
		ent->origin[0] = cl.simorg[0];
		ent->origin[1] = cl.simorg[1];
		ent->origin[2] = cl.simorg[2];
	} //we already know our exact location, don't update normally.
	//END RICH

Go up a little and replace the part under "//angles" completely with this:

	//
	// angles
	//

	//RICH
	if (j == cl.playernum) {
		ent->angles[PITCH] = -cl.simangles[PITCH]/3;
		ent->angles[YAW] = cl.simangles[YAW];
		ent->angles[ROLL] = cl.simangles[ROLL];
	} //otherwise you'll see yourself as always facing one way
	else {
		ent->angles[PITCH] = -state->viewangles[PITCH]/3;
		ent->angles[YAW] = state->viewangles[YAW];
		ent->angles[ROLL] = 0;
		ent->angles[ROLL] = V_CalcRoll (ent->angles, state->velocity)*4;
	}
	//END RICH

Go back up, and directly above the CL_LinkPlayers function, add this:

	//RICH
	extern cvar_t cl_chase;
	//END RICH


VIEW.C
------
Find the void V_CalcRefdef (void) function, and put this right above it:

	//RICH
	extern cvar_t cl_chase;
	extern cvar_t cl_chase_dist;
	extern cvar_t cl_chase_vert;
	//END RICH

Now within the function, put this right under static float oldz = 0;:

	//RICH
	int cont = 0;
	vec3_t old_vorg;
	//END RICH

Next scroll down to where you see:

	// offsets
		AngleVectors (cl.simangles, forward, right, up);

Put this right below it:

	//RICH
	if (cl_chase.value) {
		old_vorg[0] = r_refdef.vieworg[0];
		old_vorg[1] = r_refdef.vieworg[1];
		old_vorg[2] = r_refdef.vieworg[2];

		r_refdef.vieworg[0] = r_refdef.vieworg[0] - forward[0]*cl_chase_dist.value;
		r_refdef.vieworg[1] = r_refdef.vieworg[1] - forward[1]*cl_chase_dist.value;
		r_refdef.vieworg[2] = r_refdef.vieworg[2] + cl_chase_vert.value;

		cont = PM_PointContents (r_refdef.vieworg);

		while (cont == CONTENTS_SOLID) { //quick hack for view clipping
			if (r_refdef.vieworg[2] > old_vorg[2])
				r_refdef.vieworg[2]--; //Go down until we're at the original view
									   //origin to avoid clipping errors.
			if (r_refdef.vieworg[1] != old_vorg[1])
				r_refdef.vieworg[1] += forward[1]*0.1; //slowly trace back up
			if (r_refdef.vieworg[0] != old_vorg[0])
				r_refdef.vieworg[0] += forward[0]*0.1; //slowly trace back up

			cont = PM_PointContents (r_refdef.vieworg);
		}
	}
	//END RICH

Now scroll down to where you see this:

	if (view_message->flags & (PF_GIB|PF_DEAD) )
		view->model = NULL;
 	else
		view->model = cl.model_precache[cl.stats[STAT_WEAPON]];

Put this right below it:

	//RICH
	if (cl_chase.value)
		view->model = NULL; //don't show weapon in cam mode
	//END RICH


CL_MAIN.C
---------
Find the line that says cvar_t localid = {"localid", ""};, and put this
right below it:

	//RICH
	cvar_t  cl_chase = {"cl_chase", "1"};
	cvar_t  cl_chase_vert = {"cl_chase_v", "30"};
	cvar_t  cl_chase_dist = {"cl_chase_d", "50"};
	//END RICH

Now go down to void CL_Init (void) and find the line that says
Pmove_Init ();. Put this right below it:

	Cvar_RegisterVariable (&cl_chase);
	Cvar_RegisterVariable (&cl_chase_vert);
	Cvar_RegisterVariable (&cl_chase_dist);

Save everything and compile, and you should have yourself a chasecam-enabled
QuakeWorld client.

-Rich