[nq,qw] Clean up cl_view's use of the client struct

This is a huge step towards merging cl_view.
This commit is contained in:
Bill Currie 2022-02-25 15:48:57 +09:00
parent f04459f03f
commit aac9069d9f
16 changed files with 452 additions and 452 deletions

View file

@ -57,8 +57,11 @@ typedef struct movestate_s {
#define freelook (in_mlook.state & 1 || in_freelook->int_val)
struct viewstate_s;
void CL_OnFocusChange (void (*func) (int game));
void CL_Input_BuildMove (float frametime, movestate_t *state);
void CL_Input_BuildMove (float frametime, movestate_t *state,
struct viewstate_s *vs);
void CL_Input_Init (struct cbuf_s *cbuf);
void CL_Input_Init_Cvars (void);
void CL_Input_Activate (int in_game);

View file

@ -30,6 +30,7 @@
#define __client_view_h_
#include "QF/mathlib.h"
#include "QF/render.h"
#include "QF/simd/types.h"
#define INFO_CSHIFT_BONUS (1 << 0)
@ -41,12 +42,38 @@ typedef struct viewstate_s {
vec4f_t movecmd;
vec4f_t velocity;
vec4f_t origin;
vec4f_t punchangle;
vec3_t angles;
float frametime;
double time;
float height;
int weaponframe;
int onground; // -1 when in air
int active:1;
int drift_enabled:1;
int voffs_enabled:1;
int bob_enabled:1;
int intermission:1;
int force_cshifts; // bitfield of server enforced cshifts
uint32_t flags;
float frametime;
vec4f_t punchangle;
int powerup_index;
cshift_t cshifts[NUM_CSHIFTS]; // Color shifts for damage, powerups
cshift_t prev_cshifts[NUM_CSHIFTS]; // and content types
// pitch drifting vars
float idealpitch;
float pitchvel;
qboolean nodrift;
float driftmove;
double laststop;
struct model_s *weapon_model;
struct entity_s *weapon_entity;
struct entity_s *player_entity;
struct chasestate_s *chasestate;
int chase;
} viewstate_t;
#define VF_DEAD 1
@ -54,11 +81,11 @@ typedef struct viewstate_s {
void V_Init (void);
void V_Init_Cvars (void);
void V_RenderView (void);
void V_RenderView (viewstate_t *vs);
float V_CalcRoll (const vec3_t angles, vec4f_t velocity);
void V_StartPitchDrift (void);
void V_StopPitchDrift (void);
void V_StartPitchDrift (viewstate_t *vs);
void V_StopPitchDrift (viewstate_t *vs);
void V_SetContentsColor (int contents);
void V_SetContentsColor (viewstate_t *vs, int contents);
#endif // __client_view_h_

View file

@ -213,7 +213,7 @@ cvar_t *m_forward;
cvar_t *m_side;
static void
CL_AdjustAngles (float frametime, movestate_t *ms)
CL_AdjustAngles (float frametime, movestate_t *ms, viewstate_t *vs)
{
float down, up;
float pitchspeed, yawspeed;
@ -235,7 +235,7 @@ CL_AdjustAngles (float frametime, movestate_t *ms)
delta[YAW] += yawspeed * IN_ButtonState (&in_left);
}
if (in_klook.state & inb_down) {
V_StopPitchDrift ();
V_StopPitchDrift (vs);
delta[PITCH] -= pitchspeed * IN_ButtonState (&in_forward);
delta[PITCH] += pitchspeed * IN_ButtonState (&in_back);
}
@ -253,7 +253,7 @@ CL_AdjustAngles (float frametime, movestate_t *ms)
ms->angles += delta;
if (delta[PITCH]) {
V_StopPitchDrift ();
V_StopPitchDrift (vs);
ms->angles[PITCH] = bound (-70, ms->angles[PITCH], 80);
}
if (delta[ROLL]) {
@ -340,13 +340,13 @@ CL_Legacy_Init (void)
}
void
CL_Input_BuildMove (float frametime, movestate_t *state)
CL_Input_BuildMove (float frametime, movestate_t *state, viewstate_t *vs)
{
if (IN_ButtonReleased (&in_mlook) && !freelook && lookspring->int_val) {
V_StartPitchDrift ();
V_StartPitchDrift (vs);
}
CL_AdjustAngles (frametime, state);
CL_AdjustAngles (frametime, state, vs);
vec4f_t move = {};
@ -376,7 +376,7 @@ CL_Input_BuildMove (float frametime, movestate_t *state)
move[UP] -= IN_UpdateAxis (&viewdelta_position_up);
if (freelook)
V_StopPitchDrift ();
V_StopPitchDrift (vs);
//if (cl.chase
// && (chase_active->int_val == 2 || chase_active->int_val == 3)) {

View file

@ -148,9 +148,6 @@ typedef struct client_state_s {
float item_gettime[32]; // cl.time of aquiring item, for blinking
float faceanimtime; // Use anim frame if cl.time < this
cshift_t cshifts[NUM_CSHIFTS]; // Color shifts for damage, powerups
cshift_t prev_cshifts[NUM_CSHIFTS]; // and content types
// The client maintains its own idea of view angles, which are sent to the
// server each frame. The server sets punchangle when the view is temporarily
// offset, and an angle reset commands at the start of each level and after
@ -164,15 +161,7 @@ typedef struct client_state_s {
movestate_t movestate;
chasestate_t chasestate;
// pitch drifting vars
float idealpitch;
float pitchvel;
qboolean nodrift;
float driftmove;
double laststop;
qboolean paused; // Sent over by server
float viewheight;
float crouch; // Local amount for smoothing stepups
qboolean inwater;
@ -208,7 +197,6 @@ typedef struct client_state_s {
int gametype;
int maxclients;
// serverinfo mirrors
int chase;
int sv_cshifts;
int no_pogo_stick;
int teamplay;
@ -312,9 +300,8 @@ void CL_NewTranslation (int slot, struct skin_s *skin);
// view
void V_UpdatePalette (void);
void V_Register (void);
void V_ParseDamage (void);
void V_SetContentsColor (int contents);
void V_PrepBlend (void);
void V_ParseDamage (viewstate_t *vs);
void V_PrepBlend (viewstate_t *vs);
// cl_tent
void CL_SignonReply (void);

View file

@ -75,7 +75,7 @@ CL_BaseMove (usercmd_t *cmd)
return;
}
VectorCopy (cl.viewstate.angles, cl.movestate.angles);//FIXME
CL_Input_BuildMove (host_frametime, &cl.movestate);
CL_Input_BuildMove (host_frametime, &cl.movestate, &cl.viewstate);
VectorCopy (cl.movestate.angles, cl.viewstate.angles);//FIXME
memset (cmd, 0, sizeof (*cmd));

View file

@ -213,12 +213,14 @@ CL_ClearState (void)
// wipe the entire cl structure
memset (&cl, 0, sizeof (cl));
cl.chase = 1;
cl.viewstate.chase = 1;
cl.viewstate.chasestate = &cl.chasestate;
cl.watervis = 1;
r_data->force_fullscreen = 0;
r_data->lightstyle = cl.lightstyle;
CL_Init_Entity (&cl.viewent);
cl.viewstate.weapon_entity = &cl.viewent;
r_data->view_model = &cl.viewent;
SZ_Clear (&cls.message);
@ -241,7 +243,7 @@ CL_StopCshifts (void)
int i;
for (i = 0; i < NUM_CSHIFTS; i++)
cl.cshifts[i].percent = 0;
cl.viewstate.cshifts[i].percent = 0;
for (i = 0; i < MAX_CL_STATS; i++)
cl.stats[i] = 0;
}
@ -285,6 +287,7 @@ CL_Disconnect (void)
cl.worldmodel = NULL;
cl.intermission = 0;
cl.viewstate.intermission = 0;
}
void
@ -435,6 +438,7 @@ CL_ReadFromServer (void)
cl.oldtime = cl.time;
cl.time += host_frametime;
cl.viewstate.frametime = host_frametime;
cl.viewstate.time = cl.time;
do {
ret = CL_GetMessage ();
@ -496,6 +500,8 @@ CL_SetState (cactive_t state)
{
cactive_t old_state = cls.state;
cls.state = state;
cl.viewstate.active = cls.state == ca_active;
cl.viewstate.drift_enabled = !cls.demoplayback;
Sys_MaskPrintf (SYS_net, "CL_SetState: %d -> %d\n", old_state, state);
if (old_state != state) {
if (old_state == ca_active) {

View file

@ -367,6 +367,8 @@ CL_ParseServerInfo (void)
}
cl.players = Hunk_AllocName (0, cl.maxclients * sizeof (*cl.players),
"players");
cl.viewstate.voffs_enabled = cl.maxclients == 1;
cl.viewstate.bob_enabled = 1;
for (i = 0; i < cl.maxclients; i++) {
cl.players[i].userinfo = Info_ParseString ("name\\", 0, 0);
cl.players[i].name = Info_Key (cl.players[i].userinfo, "name");
@ -653,14 +655,14 @@ CL_ParseClientdata (void)
bits |= MSG_ReadByte (net_message) << 24;
if (bits & SU_VIEWHEIGHT)
cl.viewheight = ((signed char) MSG_ReadByte (net_message));
cl.viewstate.height = ((signed char) MSG_ReadByte (net_message));
else
cl.viewheight = DEFAULT_VIEWHEIGHT;
cl.viewstate.height = DEFAULT_VIEWHEIGHT;
if (bits & SU_IDEALPITCH)
cl.idealpitch = ((signed char) MSG_ReadByte (net_message));
cl.viewstate.idealpitch = ((signed char) MSG_ReadByte (net_message));
else
cl.idealpitch = 0;
cl.viewstate.idealpitch = 0;
cl.frameVelocity[1] = cl.frameVelocity[0];
vec3_t punchangle = { };
@ -691,6 +693,8 @@ CL_ParseClientdata (void)
if ((i & (1 << j)) && !(cl.stats[STAT_ITEMS] & (1 << j)))
cl.item_gettime[j] = cl.time;
cl.stats[STAT_ITEMS] = i;
#define IT_POWER (IT_QUAD | IT_SUIT | IT_INVULNERABILITY | IT_INVISIBILITY)
cl.viewstate.powerup_index = (cl.stats[STAT_ITEMS] & IT_POWER) >> 19;
}
cl.viewstate.onground = (bits & SU_ONGROUND) ? 0 : -1;
@ -700,6 +704,7 @@ CL_ParseClientdata (void)
cl.stats[STAT_WEAPONFRAME] = MSG_ReadByte (net_message);
else
cl.stats[STAT_WEAPONFRAME] = 0;
cl.viewstate.weaponframe = cl.stats[STAT_WEAPONFRAME];
if (bits & SU_ARMOR)
i = MSG_ReadByte (net_message);
@ -717,6 +722,7 @@ CL_ParseClientdata (void)
if (cl.stats[STAT_WEAPON] != i) {
cl.stats[STAT_WEAPON] = i;
Sbar_Changed ();
cl.viewstate.weapon_model = cl.model_precache[cl.stats[STAT_WEAPON]];
}
i = (short) MSG_ReadShort (net_message);
@ -914,6 +920,7 @@ CL_ParseServerMessage (void)
case svc_setview:
cl.viewentity = MSG_ReadShort (net_message);
cl.viewstate.player_entity = &cl_entities[cl.viewentity];
break;
case svc_sound:
@ -1047,7 +1054,9 @@ CL_ParseServerMessage (void)
break;
case svc_damage:
V_ParseDamage ();
V_ParseDamage (&cl.viewstate);
// put sbar face into pain frame
cl.faceanimtime = cl.time + 0.2;
break;
case svc_spawnstatic:

View file

@ -80,7 +80,7 @@ SCR_CShift (void)
cl.worldmodel);
contents = leaf->contents;
}
V_SetContentsColor (contents);
V_SetContentsColor (&cl.viewstate, contents);
r_funcs->Draw_BlendScreen (r_data->vid->cshift_color);
}
@ -164,7 +164,8 @@ CL_UpdateScreen (double realtime)
scr_funcs_normal[2] = r_funcs->SCR_DrawTurtle;
scr_funcs_normal[3] = r_funcs->SCR_DrawPause;
V_PrepBlend ();
V_RenderView ();
cl.viewstate.intermission = cl.intermission != 0;
V_PrepBlend (&cl.viewstate);
V_RenderView (&cl.viewstate);
SCR_UpdateScreen (realtime, scr_funcs[index]);
}

View file

@ -85,11 +85,37 @@ vec4f_t v_idle_yaw;
vec4f_t v_idle_roll;
vec4f_t v_idle_pitch;
cshift_t cshift_empty = { {130, 80, 50}, 0};
cshift_t cshift_water = { {130, 80, 50}, 128};
cshift_t cshift_slime = { {0, 25, 5}, 150};
cshift_t cshift_lava = { {255, 80, 0}, 150};
cshift_t cshift_bonus = { {215, 186, 60}, 50};
static cshift_t cshift_empty = { {130, 80, 50}, 0};
static cshift_t cshift_water = { {130, 80, 50}, 128};
static cshift_t cshift_slime = { { 0, 25, 5}, 150};
static cshift_t cshift_lava = { {255, 80, 0}, 150};
static cshift_t cshift_bonus = { {215, 186, 60}, 50};
static cshift_t armor_blood[] = {
{ {255, 0, 0} }, // blood
{ {220, 50, 50} }, // armor + blood
{ {200, 100, 100} }, // armor > blood need two for logic
{ {200, 100, 100} }, // armor > blood need two for logic
};
static cshift_t powerup[] = {
{ { 0, 0, 0}, 0},
{ {100, 100, 100}, 100}, // IT_INVISIBILITY
{ {255, 255, 0}, 30}, // IT_INVULNERABILITY
{ {255, 255, 0}, 30}, // IT_INVULNERABILITY
{ { 0, 255, 0}, 20}, // IT_SUIT
{ { 0, 255, 0}, 20}, // IT_SUIT
{ { 0, 255, 0}, 20}, // IT_SUIT
{ { 0, 255, 0}, 20}, // IT_SUIT
{ { 0, 0, 255}, 30}, // IT_QUAD
{ { 0, 0, 255}, 30}, // IT_QUAD
{ {255, 0, 255}, 30}, // IT_INVULNERABILITY | IT_QUAD
{ {255, 0, 255}, 30}, // IT_INVULNERABILITY | IT_QUAD
{ {255, 0, 255}, 30}, // IT_INVULNERABILITY | IT_QUAD
{ {255, 0, 255}, 30}, // IT_INVULNERABILITY | IT_QUAD
{ {255, 0, 255}, 30}, // IT_INVULNERABILITY | IT_QUAD
{ {255, 0, 255}, 30}, // IT_INVULNERABILITY | IT_QUAD
};
#define sqr(x) ((x) * (x))
@ -115,20 +141,20 @@ V_CalcRoll (const vec3_t angles, vec4f_t velocity)
}
static float
V_CalcBob (void)
V_CalcBob (viewstate_t *vs)
{
vec4f_t velocity = cl.viewstate.velocity;
vec4f_t velocity = vs->velocity;
float cycle;
static double bobtime;
static float bob;
if (cl.spectator)
if (!vs->bob_enabled)
return 0;
if (cl.viewstate.onground == -1)
if (vs->onground == -1)
return bob; // just use old value
bobtime += cl.viewstate.frametime;
bobtime += vs->frametime;
cycle = bobtime - (int) (bobtime / cl_bobcycle->value) *
cl_bobcycle->value;
cycle /= cl_bobcycle->value;
@ -150,31 +176,37 @@ V_CalcBob (void)
}
void
V_StartPitchDrift (void)
V_StartPitchDrift (viewstate_t *vs)
{
if (cl.laststop == cl.time) {
if (vs->laststop == vs->time) {
return; // something else is keeping it from drifting
}
if (cl.nodrift || !cl.pitchvel) {
cl.pitchvel = v_centerspeed->value;
cl.nodrift = false;
cl.driftmove = 0;
if (vs->nodrift || !vs->pitchvel) {
vs->pitchvel = v_centerspeed->value;
vs->nodrift = false;
vs->driftmove = 0;
}
}
void
V_StopPitchDrift (void)
static void
V_StartPitchDrift_f (void *data)
{
cl.laststop = cl.time;
cl.nodrift = true;
cl.pitchvel = 0;
V_StartPitchDrift (data);
}
void
V_StopPitchDrift (viewstate_t *vs)
{
vs->laststop = vs->time;
vs->nodrift = true;
vs->pitchvel = 0;
}
/*
V_DriftPitch
Moves the client pitch angle towards cl.idealpitch sent by the server.
Moves the client pitch angle towards vs->idealpitch sent by the server.
If the user is adjusting pitch manually, either with lookup/lookdown,
mlook and mouse, or klook and keyboard, pitch drifting is constantly
@ -184,64 +216,64 @@ V_StopPitchDrift (void)
and lookspring is non 0, or when
*/
static void
V_DriftPitch (void)
V_DriftPitch (viewstate_t *vs)
{
float delta, move;
float forwardmove = cl.viewstate.movecmd[0];
float forwardmove = vs->movecmd[0];
if (noclip_anglehack || cl.viewstate.onground == -1 || cls.demoplayback) {
cl.driftmove = 0;
cl.pitchvel = 0;
if (noclip_anglehack || vs->onground == -1 || !vs->drift_enabled) {
vs->driftmove = 0;
vs->pitchvel = 0;
return;
}
// don't count small mouse motion
if (cl.nodrift) {
if (vs->nodrift) {
if (fabs (forwardmove) < cl_forwardspeed->value)
cl.driftmove = 0;
vs->driftmove = 0;
else
cl.driftmove += cl.viewstate.frametime;
vs->driftmove += vs->frametime;
if (cl.driftmove > v_centermove->value) {
V_StartPitchDrift ();
if (vs->driftmove > v_centermove->value) {
V_StartPitchDrift (vs);
}
return;
}
delta = cl.idealpitch - cl.viewstate.angles[PITCH];
delta = vs->idealpitch - vs->angles[PITCH];
if (!delta) {
cl.pitchvel = 0;
vs->pitchvel = 0;
return;
}
move = cl.viewstate.frametime * cl.pitchvel;
cl.pitchvel += cl.viewstate.frametime * v_centerspeed->value;
move = vs->frametime * vs->pitchvel;
vs->pitchvel += vs->frametime * v_centerspeed->value;
if (delta > 0) {
if (move > delta) {
cl.pitchvel = 0;
vs->pitchvel = 0;
move = delta;
}
cl.viewstate.angles[PITCH] += move;
vs->angles[PITCH] += move;
} else if (delta < 0) {
if (move > -delta) {
cl.pitchvel = 0;
vs->pitchvel = 0;
move = -delta;
}
cl.viewstate.angles[PITCH] -= move;
vs->angles[PITCH] -= move;
}
}
/* PALETTE FLASHES */
void
V_ParseDamage (void)
V_ParseDamage (viewstate_t *vs)
{
float count, side;
int armor, blood;
vec4f_t origin = cl.viewstate.origin;
vec_t *angles = cl.viewstate.angles;
vec4f_t origin = vs->origin;
vec_t *angles = vs->angles;
vec3_t from, forward, right, up;
armor = MSG_ReadByte (net_message);
@ -252,31 +284,15 @@ V_ParseDamage (void)
if (count < 10)
count = 10;
cl.faceanimtime = cl.time + 0.2; // but sbar face into pain frame
if (cl_cshift_damage->int_val
|| (cl.sv_cshifts & INFO_CSHIFT_DAMAGE)) {
cshift_t *cshift = &cl.cshifts[CSHIFT_DAMAGE];
cshift->percent += 3 * count;
cshift->percent =
bound (0, cshift->percent, 150);
if (armor > blood) {
cshift->destcolor[0] = 200;
cshift->destcolor[1] = 100;
cshift->destcolor[2] = 100;
} else if (armor) {
cshift->destcolor[0] = 220;
cshift->destcolor[1] = 50;
cshift->destcolor[2] = 50;
} else {
cshift->destcolor[0] = 255;
cshift->destcolor[1] = 0;
cshift->destcolor[2] = 0;
}
|| (vs->force_cshifts & INFO_CSHIFT_DAMAGE)) {
cshift_t *cshift = &vs->cshifts[CSHIFT_DAMAGE];
int percent = cshift->percent;
*cshift = armor_blood[(2 * (armor > blood)) + (armor > 0)];
cshift->percent = percent + 3 * count;
cshift->percent = bound (0, cshift->percent, 150);
cshift->initialpct = cshift->percent;
cshift->time = cl.time;
cshift->time = vs->time;
}
// calculate view angle kicks
@ -309,15 +325,16 @@ V_cshift_f (void)
When you run over an item, the server sends this command
*/
static void
V_BonusFlash_f (void)
V_BonusFlash_f (void *data)
{
viewstate_t *vs = data;
if (!cl_cshift_bonus->int_val
&& !(cl.sv_cshifts & INFO_CSHIFT_BONUS))
&& !(vs->force_cshifts & INFO_CSHIFT_BONUS))
return;
cl.cshifts[CSHIFT_BONUS] = cshift_bonus;
cl.cshifts[CSHIFT_BONUS].initialpct = cl.cshifts[CSHIFT_BONUS].percent;
cl.cshifts[CSHIFT_BONUS].time = cl.time;
vs->cshifts[CSHIFT_BONUS] = cshift_bonus;
vs->cshifts[CSHIFT_BONUS].initialpct = vs->cshifts[CSHIFT_BONUS].percent;
vs->cshifts[CSHIFT_BONUS].time = vs->time;
}
/*
@ -326,69 +343,34 @@ V_BonusFlash_f (void)
Underwater, lava, etc each has a color shift
*/
void
V_SetContentsColor (int contents)
V_SetContentsColor (viewstate_t *vs, int contents)
{
if (!cl_cshift_contents->int_val
&& !(cl.sv_cshifts & INFO_CSHIFT_CONTENTS)) {
cl.cshifts[CSHIFT_CONTENTS] = cshift_empty;
&& !(vs->force_cshifts & INFO_CSHIFT_CONTENTS)) {
vs->cshifts[CSHIFT_CONTENTS] = cshift_empty;
return;
}
switch (contents) {
case CONTENTS_EMPTY:
cl.cshifts[CSHIFT_CONTENTS] = cshift_empty;
vs->cshifts[CSHIFT_CONTENTS] = cshift_empty;
break;
case CONTENTS_LAVA:
cl.cshifts[CSHIFT_CONTENTS] = cshift_lava;
vs->cshifts[CSHIFT_CONTENTS] = cshift_lava;
break;
case CONTENTS_SOLID:
case CONTENTS_SLIME:
cl.cshifts[CSHIFT_CONTENTS] = cshift_slime;
vs->cshifts[CSHIFT_CONTENTS] = cshift_slime;
break;
default:
cl.cshifts[CSHIFT_CONTENTS] = cshift_water;
vs->cshifts[CSHIFT_CONTENTS] = cshift_water;
}
}
static void
V_CalcPowerupCshift (void)
V_CalcPowerupCshift (viewstate_t *vs)
{
if (!cl.stats[STAT_ITEMS] & (IT_SUIT || IT_INVISIBILITY || IT_QUAD
|| IT_INVULNERABILITY))
{
cl.cshifts[CSHIFT_POWERUP].percent = 0;
return;
}
if (cl.stats[STAT_ITEMS] & IT_INVULNERABILITY &&
cl.stats[STAT_ITEMS] & IT_QUAD) {
cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 255;
cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 0;
cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 255;
cl.cshifts[CSHIFT_POWERUP].percent = 30;
} else if (cl.stats[STAT_ITEMS] & IT_QUAD) {
cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 0;
cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 0;
cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 255;
cl.cshifts[CSHIFT_POWERUP].percent = 30;
} else if (cl.stats[STAT_ITEMS] & IT_INVULNERABILITY) {
cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 255;
cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 255;
cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 0;
cl.cshifts[CSHIFT_POWERUP].percent = 30;
} else if (cl.stats[STAT_ITEMS] & IT_SUIT) {
cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 0;
cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 255;
cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 0;
cl.cshifts[CSHIFT_POWERUP].percent = 20;
} else if (cl.stats[STAT_ITEMS] & IT_INVISIBILITY) {
cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 100;
cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 100;
cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 100;
cl.cshifts[CSHIFT_POWERUP].percent = 100;
} else {
cl.cshifts[CSHIFT_POWERUP].percent = 0;
}
vs->cshifts[CSHIFT_POWERUP] = powerup[vs->powerup_index];
}
/*
@ -398,22 +380,22 @@ V_CalcPowerupCshift (void)
a bit, but otherwise this is his code. --KB
*/
static void
V_CalcBlend (void)
V_CalcBlend (viewstate_t *vs)
{
float a2, a3;
float r = 0, g = 0, b = 0, a = 0;
int i;
for (i = 0; i < NUM_CSHIFTS; i++) {
a2 = cl.cshifts[i].percent / 255.0;
a2 = vs->cshifts[i].percent / 255.0;
if (!a2)
continue;
a2 = min (a2, 1.0);
r += (cl.cshifts[i].destcolor[0] - r) * a2;
g += (cl.cshifts[i].destcolor[1] - g) * a2;
b += (cl.cshifts[i].destcolor[2] - b) * a2;
r += (vs->cshifts[i].destcolor[0] - r) * a2;
g += (vs->cshifts[i].destcolor[1] - g) * a2;
b += (vs->cshifts[i].destcolor[2] - b) * a2;
a3 = (1.0 - a) * (1.0 - a2);
a = 1.0 - a3;
@ -434,12 +416,12 @@ V_CalcBlend (void)
}
static void
V_DropCShift (cshift_t *cs, float droprate)
V_DropCShift (cshift_t *cs, double time, float droprate)
{
if (cs->time < 0) {
cs->percent = 0;
} else {
cs->percent = cs->initialpct - (cl.time - cs->time) * droprate;
cs->percent = cs->initialpct - (time - cs->time) * droprate;
if (cs->percent <= 0) {
cs->percent = 0;
cs->time = -1;
@ -448,56 +430,55 @@ V_DropCShift (cshift_t *cs, float droprate)
}
void
V_PrepBlend (void)
V_PrepBlend (viewstate_t *vs)
{
int i, j;
if (cl_cshift_powerup->int_val
|| (cl.sv_cshifts & INFO_CSHIFT_POWERUP))
V_CalcPowerupCshift ();
|| (vs->force_cshifts & INFO_CSHIFT_POWERUP))
V_CalcPowerupCshift (vs);
r_data->vid->cshift_changed = false;
for (i = 0; i < NUM_CSHIFTS; i++) {
if (cl.cshifts[i].percent != cl.prev_cshifts[i].percent) {
if (vs->cshifts[i].percent != vs->prev_cshifts[i].percent) {
r_data->vid->cshift_changed = true;
cl.prev_cshifts[i].percent = cl.cshifts[i].percent;
vs->prev_cshifts[i].percent = vs->cshifts[i].percent;
}
for (j = 0; j < 3; j++) {
if (cl.cshifts[i].destcolor[j] != cl.prev_cshifts[i].destcolor[j])
if (vs->cshifts[i].destcolor[j] != vs->prev_cshifts[i].destcolor[j])
{
r_data->vid->cshift_changed = true;
cl.prev_cshifts[i].destcolor[j] = cl.cshifts[i].destcolor[j];
vs->prev_cshifts[i].destcolor[j] = vs->cshifts[i].destcolor[j];
}
}
}
// drop the damage value
V_DropCShift (&cl.cshifts[CSHIFT_DAMAGE], 150);
V_DropCShift (&vs->cshifts[CSHIFT_DAMAGE], vs->time, 150);
// drop the bonus value
V_DropCShift (&cl.cshifts[CSHIFT_BONUS], 100);
V_DropCShift (&vs->cshifts[CSHIFT_BONUS], vs->time, 100);
if (!r_data->vid->cshift_changed && !r_data->vid->recalc_refdef)
return;
V_CalcBlend ();
V_CalcBlend (vs);
}
/* VIEW RENDERING */
static void
CalcGunAngle (void)
CalcGunAngle (viewstate_t *vs)
{
vec4f_t rotation = r_data->refdef->viewrotation;
//FIXME make child of camera
Transform_SetWorldRotation (cl.viewent.transform, rotation);
Transform_SetWorldRotation (vs->weapon_entity->transform, rotation);
}
static void
V_BoundOffsets (void)
V_BoundOffsets (viewstate_t *vs)
{
vec4f_t offset = r_data->refdef->viewposition
- cl.viewstate.origin;
vec4f_t offset = r_data->refdef->viewposition - vs->origin;
// absolutely bound refresh reletive to entity clipping hull
// so the view can never be inside a solid wall
@ -505,17 +486,17 @@ V_BoundOffsets (void)
offset[0] = bound (-14, offset[0], 14);
offset[1] = bound (-14, offset[1], 14);
offset[2] = bound (-22, offset[2], 30);
r_data->refdef->viewposition = cl.viewstate.origin + offset;
r_data->refdef->viewposition = vs->origin + offset;
}
static vec4f_t
idle_quat (vec4f_t axis, cvar_t *cycle, cvar_t *level)
idle_quat (vec4f_t axis, cvar_t *cycle, cvar_t *level, double time)
{
vec4f_t identity = { 0, 0, 0, 1 };
if (!level || !cycle) {
return identity;
}
float scale = sin (cl.time * cycle->value);
float scale = sin (time * cycle->value);
float ang = scale * level->value * v_idlescale->value;
float c = cos (ang * M_PI / 360);
float s = sin (ang * M_PI / 360);
@ -528,14 +509,14 @@ idle_quat (vec4f_t axis, cvar_t *cycle, cvar_t *level)
Idle swaying
*/
static void
V_AddIdle (void)
V_AddIdle (viewstate_t *vs)
{
vec4f_t roll = idle_quat ((vec4f_t) { 1, 0, 0, 0},
v_iroll_cycle, v_iroll_level);
v_iroll_cycle, v_iroll_level, vs->time);
vec4f_t pitch = idle_quat ((vec4f_t) { 0, 1, 0, 0},
v_ipitch_cycle, v_ipitch_level);
v_ipitch_cycle, v_ipitch_level, vs->time);
vec4f_t yaw = idle_quat ((vec4f_t) { 0, 0, 1, 0},
v_iyaw_cycle, v_iyaw_level);
v_iyaw_cycle, v_iyaw_level, vs->time);
vec4f_t rot = normalf (qmulf (yaw, qmulf (pitch, roll)));
// rotate the view
@ -543,8 +524,8 @@ V_AddIdle (void)
// counter-rotate the weapon
rot = qmulf (qconjf (rot),
Transform_GetWorldRotation (cl.viewent.transform));
Transform_SetWorldRotation (cl.viewent.transform, rot);
Transform_GetWorldRotation (vs->weapon_entity->transform));
Transform_SetWorldRotation (vs->weapon_entity->transform, rot);
}
/*
@ -553,10 +534,10 @@ V_AddIdle (void)
Roll is induced by movement and damage
*/
static void
V_CalcViewRoll (void)
V_CalcViewRoll (viewstate_t *vs)
{
vec_t *angles = cl.viewstate.angles;
vec4f_t velocity = cl.viewstate.velocity;
vec_t *angles = vs->angles;
vec4f_t velocity = vs->velocity;
vec3_t ang = { };
ang[ROLL] = V_CalcRoll (angles, velocity);
@ -564,10 +545,10 @@ V_CalcViewRoll (void)
if (v_dmg_time > 0) {
ang[ROLL] += v_dmg_time / v_kicktime->value * v_dmg_roll;
ang[PITCH] += v_dmg_time / v_kicktime->value * v_dmg_pitch;
v_dmg_time -= cl.viewstate.frametime;
v_dmg_time -= vs->frametime;
}
if (cl.viewstate.flags & VF_DEAD) { // VF_GIB will also set VF_DEAD
if (vs->flags & VF_DEAD) { // VF_GIB will also set VF_DEAD
ang[ROLL] = 80; // dead view angle
}
@ -577,17 +558,17 @@ V_CalcViewRoll (void)
}
static void
V_CalcIntermissionRefdef (void)
V_CalcIntermissionRefdef (viewstate_t *vs)
{
// ent is the player model (visible when out of body)
entity_t *ent = &cl_entities[cl.viewentity];
// vs->player_entity is the player model (visible when out of body)
entity_t *ent = vs->player_entity;
entity_t *view;
float old;
vec4f_t origin = Transform_GetWorldPosition (ent->transform);
vec4f_t rotation = Transform_GetWorldRotation (ent->transform);
// view is the weapon model (visible only from inside body)
view = &cl.viewent;
view = vs->weapon_entity;
r_data->refdef->viewposition = origin;
r_data->refdef->viewrotation = rotation;
@ -596,37 +577,37 @@ V_CalcIntermissionRefdef (void)
// always idle in intermission
old = v_idlescale->value;
Cvar_SetValue (v_idlescale, 1);
V_AddIdle ();
V_AddIdle (vs);
Cvar_SetValue (v_idlescale, old);
}
static void
V_CalcRefdef (void)
V_CalcRefdef (viewstate_t *vs)
{
// view is the weapon model (visible only from inside body)
entity_t *view = &cl.viewent;
entity_t *view = vs->weapon_entity;
float bob;
static float oldz = 0;
vec4f_t forward = {}, right = {}, up = {};
vec4f_t origin = cl.viewstate.origin;
vec_t *viewangles = cl.viewstate.angles;
vec4f_t origin = vs->origin;
vec_t *viewangles = vs->angles;
V_DriftPitch ();
V_DriftPitch (vs);
bob = V_CalcBob ();
bob = V_CalcBob (vs);
// refresh position
r_data->refdef->viewposition = origin;
r_data->refdef->viewposition[2] += cl.viewheight + bob;
r_data->refdef->viewposition[2] += vs->height + bob;
// never let it sit exactly on a node line, because a water plane can
// disappear when viewed with the eye exactly on it.
// server protocol specifies to only 1/8 pixel, so add 1/16 in each axis
r_data->refdef->viewposition += (vec4f_t) { 1.0/16, 1.0/16, 1.0/16, 0};
AngleQuat (cl.viewstate.angles, &r_data->refdef->viewrotation[0]);//FIXME
V_CalcViewRoll ();
V_AddIdle ();
AngleQuat (vs->angles, &r_data->refdef->viewrotation[0]);//FIXME
V_CalcViewRoll (vs);
V_AddIdle (vs);
// offsets
//FIXME semi-duplicates AngleQuat (also, vec3_t vs vec4f_t)
@ -634,18 +615,18 @@ V_CalcRefdef (void)
// don't allow cheats in multiplayer
// FIXME check for dead
if (cl.maxclients == 1) {
if (vs->voffs_enabled) {
r_data->refdef->viewposition += scr_ofsx->value * forward
+ scr_ofsy->value * right
+ scr_ofsz->value * up;
}
V_BoundOffsets ();
V_BoundOffsets (vs);
// set up gun position
CalcGunAngle ();
CalcGunAngle (vs);
origin += (vec4f_t) { 0, 0, cl.viewheight, 0 };
origin += (vec4f_t) { 0, 0, vs->height, 0 };
origin += forward * bob * 0.4f + (vec4f_t) { 0, 0, bob, 0 };
// fudge position around to keep amount of weapon visible
@ -662,23 +643,23 @@ V_CalcRefdef (void)
origin += (vec4f_t) { 0, 0, 0.5, 0};
}
model_t *model = cl.model_precache[cl.stats[STAT_WEAPON]];
model_t *model = vs->weapon_model;
if (view->renderer.model != model) {
view->animation.pose2 = -1;
}
view->renderer.model = model;
view->animation.frame = cl.stats[STAT_WEAPONFRAME];
view->animation.frame = vs->weaponframe;
view->renderer.skin = 0;
// set up the refresh position
r_data->refdef->viewrotation = qmulf (cl.viewstate.punchangle,
r_data->refdef->viewrotation = qmulf (vs->punchangle,
r_data->refdef->viewrotation);
// smooth out stair step ups
if ((cl.viewstate.onground != -1) && (origin[2] - oldz > 0)) {
if ((vs->onground != -1) && (origin[2] - oldz > 0)) {
float steptime;
steptime = cl.viewstate.frametime;
steptime = vs->frametime;
oldz += steptime * 80;
if (oldz > origin[2])
@ -696,8 +677,8 @@ V_CalcRefdef (void)
CL_TransformEntity (view, 1, ang, origin);
}
if (cl.chase && chase_active->int_val) {
Chase_Update (&cl.chasestate);
if (vs->chase && chase_active->int_val) {
Chase_Update (vs->chasestate);
}
}
@ -708,27 +689,28 @@ V_CalcRefdef (void)
the entity origin, so any view position inside that will be valid
*/
void
V_RenderView (void)
V_RenderView (viewstate_t *vs)
{
if (cls.state != ca_active) {
if (!vs->active) {
r_data->refdef->viewposition = (vec4f_t) { 0, 0, 0, 1 };
r_data->refdef->viewrotation = (vec4f_t) { 0, 0, 0, 1 };
return;
}
if (cl.intermission) { // intermission / finale rendering
V_CalcIntermissionRefdef ();
if (vs->intermission) { // intermission / finale rendering
V_CalcIntermissionRefdef (vs);
} else {
V_CalcRefdef ();
V_CalcRefdef (vs);
}
}
void
V_Init (void)
{
Cmd_AddCommand ("bf", V_BonusFlash_f, "Background flash, used when you "
"pick up an item");
Cmd_AddCommand ("centerview", V_StartPitchDrift, "Centers the player's "
Cmd_AddDataCommand ("bf", V_BonusFlash_f, &cl.viewstate,
"Background flash, used when you pick up an item");
Cmd_AddDataCommand ("centerview", V_StartPitchDrift_f, &cl.viewstate,
"Centers the player's "
"view ahead after +lookup or +lookdown\n"
"Will not work while mlook is active or freelook is 1.");
Cmd_AddCommand ("v_cshift", V_cshift_f, "This adjusts all of the colors "

View file

@ -191,9 +191,6 @@ typedef struct client_state_s {
float item_gettime[32]; // cl.time of aquiring item, for blinking
float faceanimtime; // Use anim frame if cl.time < this
cshift_t cshifts[NUM_CSHIFTS]; // Color shifts for damage, powerups
cshift_t prev_cshifts[NUM_CSHIFTS]; // and content types
// the client simulates or interpolates movement to get these values
double time; // this is the time value that the client
// is rendering at. always <= realtime
@ -202,15 +199,8 @@ typedef struct client_state_s {
viewstate_t viewstate;
movestate_t movestate;
chasestate_t chasestate;
// pitch drifting vars
float idealpitch;
float pitchvel;
qboolean nodrift;
float driftmove;
double laststop;
qboolean paused; // Sent over by server
float viewheight;
float crouch; // Local amount for smoothing stepups
qboolean inwater;
@ -249,7 +239,6 @@ typedef struct client_state_s {
int gametype;
int maxclients;
// serverinfo mirrors
int chase;
int sv_cshifts;
int no_pogo_stick;
int teamplay;
@ -327,9 +316,9 @@ void CL_UpdateScreen (double realtime);
void CL_SetState (cactive_t state);
void V_ParseDamage (void);
void V_ParseDamage (viewstate_t *vs);
void V_PrepBlend (void);
void V_PrepBlend (viewstate_t *vs);
void CL_Cmd_ForwardToServer (void);
void CL_Cmd_Init (void);

View file

@ -116,7 +116,7 @@ vectoangles (vec3_t vec, vec3_t ang)
qboolean
Cam_DrawViewModel (void)
{
if (cl.chase && chase_active->int_val)
if (cl.viewstate.chase && chase_active->int_val)
return false;
if (!cl.spectator)
@ -132,7 +132,7 @@ qboolean
Cam_DrawPlayer (int playernum)
{
if (playernum == cl.playernum) { // client player
if (cl.chase == 0 || chase_active->int_val == 0)
if (cl.viewstate.chase == 0 || chase_active->int_val == 0)
return false;
if (!cl.spectator)
return true;
@ -141,7 +141,7 @@ Cam_DrawPlayer (int playernum)
return true;
if (cl.spectator && autocam && locked && spec_track == playernum)
return false;
if (cl.chase == 0 || chase_active->int_val == 0)
if (cl.viewstate.chase == 0 || chase_active->int_val == 0)
return true;
}
return false;

View file

@ -91,7 +91,7 @@ CL_BaseMove (usercmd_t *cmd)
return;
}
VectorCopy (cl.viewstate.angles, cl.movestate.angles);//FIXME
CL_Input_BuildMove (host_frametime, &cl.movestate);
CL_Input_BuildMove (host_frametime, &cl.movestate, &cl.viewstate);
VectorCopy (cl.movestate.angles, cl.viewstate.angles);//FIXME
memset (cmd, 0, sizeof (*cmd));

View file

@ -399,6 +399,8 @@ CL_ClearState (void)
r_data->force_fullscreen = 0;
cl.maxclients = MAX_CLIENTS;
cl.viewstate.voffs_enabled = 0;
cl.viewstate.chasestate = &cl.chasestate;
// Note: we should probably hack around this and give diff values for
// diff gamedirs
@ -410,6 +412,7 @@ CL_ClearState (void)
cl.serverinfo = Info_ParseString ("", MAX_INFO_STRING, 0);
CL_Init_Entity (&cl.viewent);
cl.viewstate.weapon_entity = &cl.viewent;
Sys_MaskPrintf (SYS_dev, "Clearing memory\n");
VID_ClearMemory ();
@ -439,7 +442,7 @@ CL_StopCshifts (void)
int i;
for (i = 0; i < NUM_CSHIFTS; i++)
cl.cshifts[i].percent = 0;
cl.viewstate.cshifts[i].percent = 0;
for (i = 0; i < MAX_CL_STATS; i++)
cl.stats[i] = 0;
}
@ -618,9 +621,9 @@ CL_FullServerinfo_f (void)
Sys_Printf ("Invalid QSG Protocol number: %s", p);
}
cl.chase = cl.sv_cshifts = cl.no_pogo_stick = cl.teamplay = 0;
cl.viewstate.chase = cl.sv_cshifts = cl.no_pogo_stick = cl.teamplay = 0;
if ((p = Info_ValueForKey (cl.serverinfo, "chase")) && *p) {
cl.chase = atoi (p);
cl.viewstate.chase = atoi (p);
}
if ((p = Info_ValueForKey (cl.serverinfo, "cshifts")) && *p) {
cl.sv_cshifts = atoi (p);
@ -801,6 +804,7 @@ CL_Changing_f (void)
S_StopAllSounds ();
cl.intermission = 0;
cl.viewstate.intermission = 0;
r_data->force_fullscreen = 0;
CL_SetState (ca_connected); // not active anymore, but not
// disconnected
@ -1141,6 +1145,8 @@ CL_SetState (cactive_t state)
Sys_MaskPrintf (SYS_dev, "CL_SetState (%s)\n", state_names[state]);
cls.state = state;
cl.viewstate.active = cls.state == ca_active;
cl.viewstate.drift_enabled = !cls.demoplayback;
if (old_state != state) {
if (old_state == ca_active) {
// leaving active state

View file

@ -831,6 +831,8 @@ CL_ParseServerData (void)
}
cl.viewentity = cl.playernum + 1;
cl.viewstate.player_entity = &cl_entities[cl.viewentity];
cl.viewstate.bob_enabled = !cl.spectator;
// get the full level name
str = MSG_ReadString (net_message);
@ -1216,7 +1218,7 @@ CL_ServerInfo (void)
Info_SetValueForKey (cl.serverinfo, key, value, 0);
if (strequal (key, "chase")) {
cl.chase = atoi (value);
cl.viewstate.chase = atoi (value);
} else if (strequal (key, "cshifts")) {
cl.sv_cshifts = atoi (value);
} else if (strequal (key, "no_pogo_stick")) {
@ -1260,6 +1262,8 @@ CL_SetStat (int stat, int value)
switch (stat) {
case STAT_ITEMS:
Sbar_Changed ();
#define IT_POWER (IT_QUAD | IT_SUIT | IT_INVULNERABILITY | IT_INVISIBILITY)
cl.viewstate.powerup_index = (cl.stats[STAT_ITEMS]&IT_POWER) >> 19;
break;
case STAT_HEALTH:
if (cl_player_health_e->func)
@ -1270,6 +1274,7 @@ CL_SetStat (int stat, int value)
break;
}
cl.stats[stat] = value;
cl.viewstate.weapon_model = cl.model_precache[cl.stats[STAT_WEAPON]];
}
static void
@ -1501,7 +1506,9 @@ CL_ParseServerMessage (void)
// svc_particle
case svc_damage:
V_ParseDamage ();
V_ParseDamage (&cl.viewstate);
// put sbar face into pain frame
cl.faceanimtime = cl.time + 0.2;
break;
case svc_spawnstatic:

View file

@ -81,7 +81,7 @@ SCR_CShift (void)
cl.worldmodel);
contents = leaf->contents;
}
V_SetContentsColor (contents);
V_SetContentsColor (&cl.viewstate, contents);
r_funcs->Draw_BlendScreen (r_data->vid->cshift_color);
}
@ -186,7 +186,18 @@ CL_UpdateScreen (double realtime)
scr_funcs_normal[2] = r_funcs->SCR_DrawTurtle;
scr_funcs_normal[3] = r_funcs->SCR_DrawPause;
V_PrepBlend ();
V_RenderView ();
if (cl.viewstate.flags & VF_GIB) {
cl.viewstate.height = 8; // gib view height
} else if (cl.viewstate.flags & VF_DEAD) {
cl.viewstate.height = -16; // corpse view height
} else {
cl.viewstate.height = DEFAULT_VIEWHEIGHT; // view height
if (cl.stdver)
cl.viewstate.height = cl.stats[STAT_VIEWHEIGHT];
}
cl.viewstate.intermission = cl.intermission != 0;
V_PrepBlend (&cl.viewstate);
V_RenderView (&cl.viewstate);
SCR_UpdateScreen (realtime, scr_funcs[index]);
}

View file

@ -86,11 +86,37 @@ vec4f_t v_idle_yaw;
vec4f_t v_idle_roll;
vec4f_t v_idle_pitch;
cshift_t cshift_empty = { {130, 80, 50}, 0};
cshift_t cshift_water = { {130, 80, 50}, 128};
cshift_t cshift_slime = { {0, 25, 5}, 150};
cshift_t cshift_lava = { {255, 80, 0}, 150};
cshift_t cshift_bonus = { {215, 186, 60}, 50};
static cshift_t cshift_empty = { {130, 80, 50}, 0};
static cshift_t cshift_water = { {130, 80, 50}, 128};
static cshift_t cshift_slime = { { 0, 25, 5}, 150};
static cshift_t cshift_lava = { {255, 80, 0}, 150};
static cshift_t cshift_bonus = { {215, 186, 60}, 50};
static cshift_t armor_blood[] = {
{ {255, 0, 0} }, // blood
{ {220, 50, 50} }, // armor + blood
{ {200, 100, 100} }, // armor > blood need two for logic
{ {200, 100, 100} }, // armor > blood need two for logic
};
static cshift_t powerup[] = {
{ { 0, 0, 0}, 0},
{ {100, 100, 100}, 100}, // IT_INVISIBILITY
{ {255, 255, 0}, 30}, // IT_INVULNERABILITY
{ {255, 255, 0}, 30}, // IT_INVULNERABILITY
{ { 0, 255, 0}, 20}, // IT_SUIT
{ { 0, 255, 0}, 20}, // IT_SUIT
{ { 0, 255, 0}, 20}, // IT_SUIT
{ { 0, 255, 0}, 20}, // IT_SUIT
{ { 0, 0, 255}, 30}, // IT_QUAD
{ { 0, 0, 255}, 30}, // IT_QUAD
{ {255, 0, 255}, 30}, // IT_INVULNERABILITY | IT_QUAD
{ {255, 0, 255}, 30}, // IT_INVULNERABILITY | IT_QUAD
{ {255, 0, 255}, 30}, // IT_INVULNERABILITY | IT_QUAD
{ {255, 0, 255}, 30}, // IT_INVULNERABILITY | IT_QUAD
{ {255, 0, 255}, 30}, // IT_INVULNERABILITY | IT_QUAD
{ {255, 0, 255}, 30}, // IT_INVULNERABILITY | IT_QUAD
};
#define sqr(x) ((x) * (x))
@ -116,20 +142,20 @@ V_CalcRoll (const vec3_t angles, vec4f_t velocity)
}
static float
V_CalcBob (void)
V_CalcBob (viewstate_t *vs)
{
vec4f_t velocity = cl.viewstate.velocity;
vec4f_t velocity = vs->velocity;
float cycle;
static double bobtime;
static float bob;
if (cl.spectator)
if (!vs->bob_enabled)
return 0;
if (cl.viewstate.onground == -1)
if (vs->onground == -1)
return bob; // just use old value
bobtime += cl.viewstate.frametime;
bobtime += vs->frametime;
cycle = bobtime - (int) (bobtime / cl_bobcycle->value) *
cl_bobcycle->value;
cycle /= cl_bobcycle->value;
@ -151,31 +177,37 @@ V_CalcBob (void)
}
void
V_StartPitchDrift (void)
V_StartPitchDrift (viewstate_t *vs)
{
if (cl.laststop == cl.time) {
if (vs->laststop == vs->time) {
return; // something else is keeping it from drifting
}
if (cl.nodrift || !cl.pitchvel) {
cl.pitchvel = v_centerspeed->value;
cl.nodrift = false;
cl.driftmove = 0;
if (vs->nodrift || !vs->pitchvel) {
vs->pitchvel = v_centerspeed->value;
vs->nodrift = false;
vs->driftmove = 0;
}
}
void
V_StopPitchDrift (void)
static void
V_StartPitchDrift_f (void *data)
{
cl.laststop = cl.time;
cl.nodrift = true;
cl.pitchvel = 0;
V_StartPitchDrift (data);
}
void
V_StopPitchDrift (viewstate_t *vs)
{
vs->laststop = vs->time;
vs->nodrift = true;
vs->pitchvel = 0;
}
/*
V_DriftPitch
Moves the client pitch angle towards cl.idealpitch sent by the server.
Moves the client pitch angle towards vs->idealpitch sent by the server.
If the user is adjusting pitch manually, either with lookup/lookdown,
mlook and mouse, or klook and keyboard, pitch drifting is constantly
@ -185,64 +217,64 @@ V_StopPitchDrift (void)
and lookspring is non 0, or when
*/
static void
V_DriftPitch (void)
V_DriftPitch (viewstate_t *vs)
{
float delta, move;
float forwardmove = cl.viewstate.movecmd[0];
float forwardmove = vs->movecmd[0];
if (noclip_anglehack || cl.viewstate.onground == -1 || cls.demoplayback) {
cl.driftmove = 0;
cl.pitchvel = 0;
if (noclip_anglehack || vs->onground == -1 || !vs->drift_enabled) {
vs->driftmove = 0;
vs->pitchvel = 0;
return;
}
// don't count small mouse motion
if (cl.nodrift) {
if (vs->nodrift) {
if (fabs (forwardmove) < cl_forwardspeed->value)
cl.driftmove = 0;
vs->driftmove = 0;
else
cl.driftmove += cl.viewstate.frametime;
vs->driftmove += vs->frametime;
if (cl.driftmove > v_centermove->value) {
V_StartPitchDrift ();
if (vs->driftmove > v_centermove->value) {
V_StartPitchDrift (vs);
}
return;
}
delta = cl.idealpitch - cl.viewstate.angles[PITCH];
delta = vs->idealpitch - vs->angles[PITCH];
if (!delta) {
cl.pitchvel = 0;
vs->pitchvel = 0;
return;
}
move = cl.viewstate.frametime * cl.pitchvel;
cl.pitchvel += cl.viewstate.frametime * v_centerspeed->value;
move = vs->frametime * vs->pitchvel;
vs->pitchvel += vs->frametime * v_centerspeed->value;
if (delta > 0) {
if (move > delta) {
cl.pitchvel = 0;
vs->pitchvel = 0;
move = delta;
}
cl.viewstate.angles[PITCH] += move;
vs->angles[PITCH] += move;
} else if (delta < 0) {
if (move > -delta) {
cl.pitchvel = 0;
vs->pitchvel = 0;
move = -delta;
}
cl.viewstate.angles[PITCH] -= move;
vs->angles[PITCH] -= move;
}
}
/* PALETTE FLASHES */
void
V_ParseDamage (void)
V_ParseDamage (viewstate_t *vs)
{
float count, side;
int armor, blood;
vec4f_t origin = cl.viewstate.origin;
vec_t *angles = cl.viewstate.angles;
vec4f_t origin = vs->origin;
vec_t *angles = vs->angles;
vec3_t from, forward, right, up;
armor = MSG_ReadByte (net_message);
@ -253,31 +285,15 @@ V_ParseDamage (void)
if (count < 10)
count = 10;
cl.faceanimtime = cl.time + 0.2; // but sbar face into pain frame
if (cl_cshift_damage->int_val
|| (cl.sv_cshifts & INFO_CSHIFT_DAMAGE)) {
cshift_t *cshift = &cl.cshifts[CSHIFT_DAMAGE];
cshift->percent += 3 * count;
cshift->percent =
bound (0, cshift->percent, 150);
if (armor > blood) {
cshift->destcolor[0] = 200;
cshift->destcolor[1] = 100;
cshift->destcolor[2] = 100;
} else if (armor) {
cshift->destcolor[0] = 220;
cshift->destcolor[1] = 50;
cshift->destcolor[2] = 50;
} else {
cshift->destcolor[0] = 255;
cshift->destcolor[1] = 0;
cshift->destcolor[2] = 0;
}
|| (vs->force_cshifts & INFO_CSHIFT_DAMAGE)) {
cshift_t *cshift = &vs->cshifts[CSHIFT_DAMAGE];
int percent = cshift->percent;
*cshift = armor_blood[(2 * (armor > blood)) + (armor > 0)];
cshift->percent = percent + 3 * count;
cshift->percent = bound (0, cshift->percent, 150);
cshift->initialpct = cshift->percent;
cshift->time = cl.time;
cshift->time = vs->time;
}
// calculate view angle kicks
@ -310,15 +326,16 @@ V_cshift_f (void)
When you run over an item, the server sends this command
*/
static void
V_BonusFlash_f (void)
V_BonusFlash_f (void *data)
{
viewstate_t *vs = data;
if (!cl_cshift_bonus->int_val
&& !(cl.sv_cshifts & INFO_CSHIFT_BONUS))
&& !(vs->force_cshifts & INFO_CSHIFT_BONUS))
return;
cl.cshifts[CSHIFT_BONUS] = cshift_bonus;
cl.cshifts[CSHIFT_BONUS].initialpct = cl.cshifts[CSHIFT_BONUS].percent;
cl.cshifts[CSHIFT_BONUS].time = cl.time;
vs->cshifts[CSHIFT_BONUS] = cshift_bonus;
vs->cshifts[CSHIFT_BONUS].initialpct = vs->cshifts[CSHIFT_BONUS].percent;
vs->cshifts[CSHIFT_BONUS].time = vs->time;
}
/*
@ -327,69 +344,34 @@ V_BonusFlash_f (void)
Underwater, lava, etc each has a color shift
*/
void
V_SetContentsColor (int contents)
V_SetContentsColor (viewstate_t *vs, int contents)
{
if (!cl_cshift_contents->int_val
&& !(cl.sv_cshifts & INFO_CSHIFT_CONTENTS)) {
cl.cshifts[CSHIFT_CONTENTS] = cshift_empty;
&& !(vs->force_cshifts & INFO_CSHIFT_CONTENTS)) {
vs->cshifts[CSHIFT_CONTENTS] = cshift_empty;
return;
}
switch (contents) {
case CONTENTS_EMPTY:
cl.cshifts[CSHIFT_CONTENTS] = cshift_empty;
vs->cshifts[CSHIFT_CONTENTS] = cshift_empty;
break;
case CONTENTS_LAVA:
cl.cshifts[CSHIFT_CONTENTS] = cshift_lava;
vs->cshifts[CSHIFT_CONTENTS] = cshift_lava;
break;
case CONTENTS_SOLID:
case CONTENTS_SLIME:
cl.cshifts[CSHIFT_CONTENTS] = cshift_slime;
vs->cshifts[CSHIFT_CONTENTS] = cshift_slime;
break;
default:
cl.cshifts[CSHIFT_CONTENTS] = cshift_water;
vs->cshifts[CSHIFT_CONTENTS] = cshift_water;
}
}
static void
V_CalcPowerupCshift (void)
V_CalcPowerupCshift (viewstate_t *vs)
{
if (!cl.stats[STAT_ITEMS] & (IT_SUIT || IT_INVISIBILITY || IT_QUAD
|| IT_INVULNERABILITY))
{
cl.cshifts[CSHIFT_POWERUP].percent = 0;
return;
}
if (cl.stats[STAT_ITEMS] & IT_INVULNERABILITY &&
cl.stats[STAT_ITEMS] & IT_QUAD) {
cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 255;
cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 0;
cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 255;
cl.cshifts[CSHIFT_POWERUP].percent = 30;
} else if (cl.stats[STAT_ITEMS] & IT_QUAD) {
cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 0;
cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 0;
cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 255;
cl.cshifts[CSHIFT_POWERUP].percent = 30;
} else if (cl.stats[STAT_ITEMS] & IT_INVULNERABILITY) {
cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 255;
cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 255;
cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 0;
cl.cshifts[CSHIFT_POWERUP].percent = 30;
} else if (cl.stats[STAT_ITEMS] & IT_SUIT) {
cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 0;
cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 255;
cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 0;
cl.cshifts[CSHIFT_POWERUP].percent = 20;
} else if (cl.stats[STAT_ITEMS] & IT_INVISIBILITY) {
cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 100;
cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 100;
cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 100;
cl.cshifts[CSHIFT_POWERUP].percent = 100;
} else {
cl.cshifts[CSHIFT_POWERUP].percent = 0;
}
vs->cshifts[CSHIFT_POWERUP] = powerup[vs->powerup_index];
}
/*
@ -399,22 +381,22 @@ V_CalcPowerupCshift (void)
a bit, but otherwise this is his code. --KB
*/
static void
V_CalcBlend (void)
V_CalcBlend (viewstate_t *vs)
{
float a2, a3;
float r = 0, g = 0, b = 0, a = 0;
int i;
for (i = 0; i < NUM_CSHIFTS; i++) {
a2 = cl.cshifts[i].percent / 255.0;
a2 = vs->cshifts[i].percent / 255.0;
if (!a2)
continue;
a2 = min (a2, 1.0);
r += (cl.cshifts[i].destcolor[0] - r) * a2;
g += (cl.cshifts[i].destcolor[1] - g) * a2;
b += (cl.cshifts[i].destcolor[2] - b) * a2;
r += (vs->cshifts[i].destcolor[0] - r) * a2;
g += (vs->cshifts[i].destcolor[1] - g) * a2;
b += (vs->cshifts[i].destcolor[2] - b) * a2;
a3 = (1.0 - a) * (1.0 - a2);
a = 1.0 - a3;
@ -435,12 +417,12 @@ V_CalcBlend (void)
}
static void
V_DropCShift (cshift_t *cs, float droprate)
V_DropCShift (cshift_t *cs, double time, float droprate)
{
if (cs->time < 0) {
cs->percent = 0;
} else {
cs->percent = cs->initialpct - (cl.time - cs->time) * droprate;
cs->percent = cs->initialpct - (time - cs->time) * droprate;
if (cs->percent <= 0) {
cs->percent = 0;
cs->time = -1;
@ -449,56 +431,55 @@ V_DropCShift (cshift_t *cs, float droprate)
}
void
V_PrepBlend (void)
V_PrepBlend (viewstate_t *vs)
{
int i, j;
if (cl_cshift_powerup->int_val
|| (cl.sv_cshifts & INFO_CSHIFT_POWERUP))
V_CalcPowerupCshift ();
|| (vs->force_cshifts & INFO_CSHIFT_POWERUP))
V_CalcPowerupCshift (vs);
r_data->vid->cshift_changed = false;
for (i = 0; i < NUM_CSHIFTS; i++) {
if (cl.cshifts[i].percent != cl.prev_cshifts[i].percent) {
if (vs->cshifts[i].percent != vs->prev_cshifts[i].percent) {
r_data->vid->cshift_changed = true;
cl.prev_cshifts[i].percent = cl.cshifts[i].percent;
vs->prev_cshifts[i].percent = vs->cshifts[i].percent;
}
for (j = 0; j < 3; j++) {
if (cl.cshifts[i].destcolor[j] != cl.prev_cshifts[i].destcolor[j])
if (vs->cshifts[i].destcolor[j] != vs->prev_cshifts[i].destcolor[j])
{
r_data->vid->cshift_changed = true;
cl.prev_cshifts[i].destcolor[j] = cl.cshifts[i].destcolor[j];
vs->prev_cshifts[i].destcolor[j] = vs->cshifts[i].destcolor[j];
}
}
}
// drop the damage value
V_DropCShift (&cl.cshifts[CSHIFT_DAMAGE], 150);
V_DropCShift (&vs->cshifts[CSHIFT_DAMAGE], vs->time, 150);
// drop the bonus value
V_DropCShift (&cl.cshifts[CSHIFT_BONUS], 100);
V_DropCShift (&vs->cshifts[CSHIFT_BONUS], vs->time, 100);
if (!r_data->vid->cshift_changed && !r_data->vid->recalc_refdef)
return;
V_CalcBlend ();
V_CalcBlend (vs);
}
/* VIEW RENDERING */
static void
CalcGunAngle (void)
CalcGunAngle (viewstate_t *vs)
{
vec4f_t rotation = r_data->refdef->viewrotation;
//FIXME make child of camera
Transform_SetWorldRotation (cl.viewent.transform, rotation);
Transform_SetWorldRotation (vs->weapon_entity->transform, rotation);
}
static void
V_BoundOffsets (void)
V_BoundOffsets (viewstate_t *vs)
{
vec4f_t offset = r_data->refdef->viewposition
- cl.viewstate.origin;
vec4f_t offset = r_data->refdef->viewposition - vs->origin;
// absolutely bound refresh reletive to entity clipping hull
// so the view can never be inside a solid wall
@ -506,17 +487,17 @@ V_BoundOffsets (void)
offset[0] = bound (-14, offset[0], 14);
offset[1] = bound (-14, offset[1], 14);
offset[2] = bound (-22, offset[2], 30);
r_data->refdef->viewposition = cl.viewstate.origin + offset;
r_data->refdef->viewposition = vs->origin + offset;
}
static vec4f_t
idle_quat (vec4f_t axis, cvar_t *cycle, cvar_t *level)
idle_quat (vec4f_t axis, cvar_t *cycle, cvar_t *level, double time)
{
vec4f_t identity = { 0, 0, 0, 1 };
if (!level || !cycle) {
return identity;
}
float scale = sin (cl.time * cycle->value);
float scale = sin (time * cycle->value);
float ang = scale * level->value * v_idlescale->value;
float c = cos (ang * M_PI / 360);
float s = sin (ang * M_PI / 360);
@ -529,14 +510,14 @@ idle_quat (vec4f_t axis, cvar_t *cycle, cvar_t *level)
Idle swaying
*/
static void
V_AddIdle (void)
V_AddIdle (viewstate_t *vs)
{
vec4f_t roll = idle_quat ((vec4f_t) { 1, 0, 0, 0},
v_iroll_cycle, v_iroll_level);
v_iroll_cycle, v_iroll_level, vs->time);
vec4f_t pitch = idle_quat ((vec4f_t) { 0, 1, 0, 0},
v_ipitch_cycle, v_ipitch_level);
v_ipitch_cycle, v_ipitch_level, vs->time);
vec4f_t yaw = idle_quat ((vec4f_t) { 0, 0, 1, 0},
v_iyaw_cycle, v_iyaw_level);
v_iyaw_cycle, v_iyaw_level, vs->time);
vec4f_t rot = normalf (qmulf (yaw, qmulf (pitch, roll)));
// rotate the view
@ -544,8 +525,8 @@ V_AddIdle (void)
// counter-rotate the weapon
rot = qmulf (qconjf (rot),
Transform_GetWorldRotation (cl.viewent.transform));
Transform_SetWorldRotation (cl.viewent.transform, rot);
Transform_GetWorldRotation (vs->weapon_entity->transform));
Transform_SetWorldRotation (vs->weapon_entity->transform, rot);
}
/*
@ -554,10 +535,10 @@ V_AddIdle (void)
Roll is induced by movement and damage
*/
static void
V_CalcViewRoll (void)
V_CalcViewRoll (viewstate_t *vs)
{
vec_t *angles = cl.viewstate.angles;
vec4f_t velocity = cl.viewstate.velocity;
vec_t *angles = vs->angles;
vec4f_t velocity = vs->velocity;
vec3_t ang = { };
ang[ROLL] = V_CalcRoll (angles, velocity);
@ -565,10 +546,10 @@ V_CalcViewRoll (void)
if (v_dmg_time > 0) {
ang[ROLL] += v_dmg_time / v_kicktime->value * v_dmg_roll;
ang[PITCH] += v_dmg_time / v_kicktime->value * v_dmg_pitch;
v_dmg_time -= cl.viewstate.frametime;
v_dmg_time -= vs->frametime;
}
if (cl.viewstate.flags & VF_DEAD) { // VF_GIB will also set VF_DEAD
if (vs->flags & VF_DEAD) { // VF_GIB will also set VF_DEAD
ang[ROLL] = 80; // dead view angle
}
@ -578,17 +559,17 @@ V_CalcViewRoll (void)
}
static void
V_CalcIntermissionRefdef (void)
V_CalcIntermissionRefdef (viewstate_t *vs)
{
// ent is the player model (visible when out of body)
entity_t *ent = &cl_entities[cl.viewentity];
// vs->player_entity is the player model (visible when out of body)
entity_t *ent = vs->player_entity;
entity_t *view;
float old;
vec4f_t origin = Transform_GetWorldPosition (ent->transform);
vec4f_t rotation = Transform_GetWorldRotation (ent->transform);
// view is the weapon model (visible only from inside body)
view = &cl.viewent;
view = vs->weapon_entity;
r_data->refdef->viewposition = origin;
r_data->refdef->viewrotation = rotation;
@ -597,37 +578,37 @@ V_CalcIntermissionRefdef (void)
// always idle in intermission
old = v_idlescale->value;
Cvar_SetValue (v_idlescale, 1);
V_AddIdle ();
V_AddIdle (vs);
Cvar_SetValue (v_idlescale, old);
}
static void
V_CalcRefdef (void)
V_CalcRefdef (viewstate_t *vs)
{
// view is the weapon model (visible only from inside body)
entity_t *view = &cl.viewent;
entity_t *view = vs->weapon_entity;
float bob;
static float oldz = 0;
vec4f_t forward = {}, right = {}, up = {};
vec4f_t origin = cl.viewstate.origin;
vec_t *viewangles = cl.viewstate.angles;
vec4f_t origin = vs->origin;
vec_t *viewangles = vs->angles;
V_DriftPitch ();
V_DriftPitch (vs);
bob = V_CalcBob ();
bob = V_CalcBob (vs);
// refresh position
r_data->refdef->viewposition = origin;
r_data->refdef->viewposition[2] += cl.viewheight + bob;
r_data->refdef->viewposition[2] += vs->height + bob;
// never let it sit exactly on a node line, because a water plane can
// disappear when viewed with the eye exactly on it.
// server protocol specifies to only 1/8 pixel, so add 1/16 in each axis
r_data->refdef->viewposition += (vec4f_t) { 1.0/16, 1.0/16, 1.0/16, 0};
AngleQuat (cl.viewstate.angles, &r_data->refdef->viewrotation[0]);//FIXME
V_CalcViewRoll ();
V_AddIdle ();
AngleQuat (vs->angles, &r_data->refdef->viewrotation[0]);//FIXME
V_CalcViewRoll (vs);
V_AddIdle (vs);
// offsets
//FIXME semi-duplicates AngleQuat (also, vec3_t vs vec4f_t)
@ -635,18 +616,18 @@ V_CalcRefdef (void)
// don't allow cheats in multiplayer
// FIXME check for dead
if (cl.maxclients == 1) {
if (vs->voffs_enabled) {
r_data->refdef->viewposition += scr_ofsx->value * forward
+ scr_ofsy->value * right
+ scr_ofsz->value * up;
}
V_BoundOffsets ();
V_BoundOffsets (vs);
// set up gun position
CalcGunAngle ();
CalcGunAngle (vs);
origin += (vec4f_t) { 0, 0, cl.viewheight, 0 };
origin += (vec4f_t) { 0, 0, vs->height, 0 };
origin += forward * bob * 0.4f + (vec4f_t) { 0, 0, bob, 0 };
// fudge position around to keep amount of weapon visible
@ -663,26 +644,26 @@ V_CalcRefdef (void)
origin += (vec4f_t) { 0, 0, 0.5, 0};
}
model_t *model = cl.model_precache[cl.stats[STAT_WEAPON]];
if (cl.viewstate.flags & (VF_GIB | VF_DEAD)) {
model_t *model = vs->weapon_model;
if (vs->flags & (VF_GIB | VF_DEAD)) {
model = NULL;
}
if (view->renderer.model != model) {
view->animation.pose2 = -1;
}
view->renderer.model = model;
view->animation.frame = cl.viewstate.weaponframe;
view->animation.frame = vs->weaponframe;
view->renderer.skin = 0;
// set up the refresh position
r_data->refdef->viewrotation = qmulf (cl.viewstate.punchangle,
r_data->refdef->viewrotation = qmulf (vs->punchangle,
r_data->refdef->viewrotation);
// smooth out stair step ups
if ((cl.viewstate.onground != -1) && (origin[2] - oldz > 0)) {
if ((vs->onground != -1) && (origin[2] - oldz > 0)) {
float steptime;
steptime = cl.viewstate.frametime;
steptime = vs->frametime;
oldz += steptime * 80;
if (oldz > origin[2])
@ -700,29 +681,29 @@ V_CalcRefdef (void)
CL_TransformEntity (view, 1, ang, origin);
}
if (cl.chase && chase_active->int_val) {
Chase_Update (&cl.chasestate);
if (vs->chase && chase_active->int_val) {
Chase_Update (vs->chasestate);
}
}
static void
DropPunchAngle (void)
DropPunchAngle (viewstate_t *vs)
{
vec4f_t punch = cl.viewstate.punchangle;
vec4f_t punch = vs->punchangle;
float ps = magnitude3f (punch)[0];
if (ps < 1e-3) {
// < 0.2 degree rotation, not worth worrying about
//ensure the quaternion is normalized
cl.viewstate.punchangle = (vec4f_t) { 0, 0, 0, 1 };
vs->punchangle = (vec4f_t) { 0, 0, 0, 1 };
return;
}
float pc = punch[3];
float ds = 0.0871557427 * cl.viewstate.frametime;
float ds = 0.0871557427 * vs->frametime;
float dc = sqrt (1 - ds * ds);
float s = ps * dc - pc * ds;
float c = pc * dc + ps * ds;
if (s <= 0 || c >= 1) {
cl.viewstate.punchangle = (vec4f_t) { 0, 0, 0, 1 };
vs->punchangle = (vec4f_t) { 0, 0, 0, 1 };
} else {
punch *= s / ps;
punch[3] = c;
@ -736,38 +717,29 @@ DropPunchAngle (void)
the entity origin, so any view position inside that will be valid
*/
void
V_RenderView (void)
V_RenderView (viewstate_t *vs)
{
if (cls.state != ca_active) {
if (!vs->active) {
r_data->refdef->viewposition = (vec4f_t) { 0, 0, 0, 1 };
r_data->refdef->viewrotation = (vec4f_t) { 0, 0, 0, 1 };
return;
}
if (cl.viewstate.flags & VF_GIB) {
cl.viewheight = 8; // gib view height
} else if (cl.viewstate.flags & VF_DEAD) {
cl.viewheight = -16; // corpse view height
DropPunchAngle (vs);
if (vs->intermission) { // intermission / finale rendering
V_CalcIntermissionRefdef (vs);
} else {
cl.viewheight = DEFAULT_VIEWHEIGHT; // view height
if (cl.stdver)
cl.viewheight = cl.stats[STAT_VIEWHEIGHT];
}
DropPunchAngle ();
if (cl.intermission) { // intermission / finale rendering
V_CalcIntermissionRefdef ();
} else {
V_CalcRefdef ();
V_CalcRefdef (vs);
}
}
void
V_Init (void)
{
Cmd_AddCommand ("bf", V_BonusFlash_f, "Background flash, used when you "
"pick up an item");
Cmd_AddCommand ("centerview", V_StartPitchDrift, "Centers the player's "
Cmd_AddDataCommand ("bf", V_BonusFlash_f, &cl.viewstate,
"Background flash, used when you pick up an item");
Cmd_AddDataCommand ("centerview", V_StartPitchDrift_f, &cl.viewstate,
"Centers the player's "
"view ahead after +lookup or +lookdown\n"
"Will not work while mlook is active or freelook is 1.");
Cmd_AddCommand ("v_cshift", V_cshift_f, "This adjusts all of the colors "