2022-12-17 13:32:43 +01:00
|
|
|
// view/refresh setup functions
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
#include "hud.h"
|
|
|
|
#include "cl_util.h"
|
|
|
|
#include "cvardef.h"
|
|
|
|
#include "usercmd.h"
|
|
|
|
#include "const.h"
|
|
|
|
|
|
|
|
#include "entity_state.h"
|
|
|
|
#include "cl_entity.h"
|
|
|
|
#include "ref_params.h"
|
|
|
|
#include "in_defs.h" // PITCH YAW ROLL
|
|
|
|
#include "pm_movevars.h"
|
|
|
|
#include "pm_shared.h"
|
|
|
|
#include "pm_defs.h"
|
|
|
|
#include "event_api.h"
|
|
|
|
#include "pmtrace.h"
|
|
|
|
#include "screenfade.h"
|
|
|
|
#include "shake.h"
|
|
|
|
#include "hltv.h"
|
|
|
|
#include "Exports.h"
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
int CL_IsThirdPerson();
|
|
|
|
void CL_CameraOffset(float* ofs);
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
void DLLEXPORT V_CalcRefdef(struct ref_params_s* pparams);
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
void PM_ParticleLine(float* start, float* end, int pcolor, float life, float vert);
|
|
|
|
int PM_GetVisEntInfo(int ent);
|
|
|
|
int PM_GetPhysEntInfo(int ent);
|
|
|
|
void InterpolateAngles(float* start, float* end, float* output, float frac);
|
|
|
|
void NormalizeAngles(float* angles);
|
|
|
|
float Distance(const float* v1, const float* v2);
|
|
|
|
float AngleBetweenVectors(const float* v1, const float* v2);
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
extern float vJumpOrigin[3];
|
|
|
|
extern float vJumpAngles[3];
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
void V_DropPunchAngle(float frametime, float* ev_punchangle);
|
|
|
|
void VectorAngles(const float* forward, float* angles);
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
#include "r_studioint.h"
|
|
|
|
#include "com_model.h"
|
|
|
|
#include "kbutton.h"
|
|
|
|
|
|
|
|
extern engine_studio_api_t IEngineStudio;
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
extern kbutton_t in_mlook;
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
/*
|
|
|
|
The view is allowed to move slightly from it's true position for bobbing,
|
|
|
|
but if it exceeds 8 pixels linear distance (spherical, not box), the list of
|
|
|
|
entities sent from the server may not include everything in the pvs, especially
|
|
|
|
when crossing a water boudnary.
|
|
|
|
*/
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
extern cvar_t* cl_forwardspeed;
|
|
|
|
extern cvar_t* chase_active;
|
|
|
|
extern cvar_t *scr_ofsx, *scr_ofsy, *scr_ofsz;
|
|
|
|
extern cvar_t* cl_vsmoothing;
|
2021-02-07 14:09:34 +01:00
|
|
|
extern cvar_t* cl_rollangle;
|
|
|
|
extern cvar_t* cl_rollspeed;
|
2021-02-08 12:41:02 +01:00
|
|
|
extern cvar_t* cl_bobtilt;
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
#define CAM_MODE_RELAX 1
|
|
|
|
#define CAM_MODE_FOCUS 2
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
Vector v_origin, v_angles, v_cl_angles, v_sim_org, v_lastAngles;
|
|
|
|
float v_frametime, v_lastDistance;
|
|
|
|
float v_cameraRelaxAngle = 5.0f;
|
|
|
|
float v_cameraFocusAngle = 35.0f;
|
|
|
|
int v_cameraMode = CAM_MODE_FOCUS;
|
2021-11-28 15:32:26 +01:00
|
|
|
bool v_resetCamera = true;
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-03-16 19:45:46 +01:00
|
|
|
Vector v_client_aimangles;
|
|
|
|
Vector ev_punchangle;
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
cvar_t* scr_ofsx;
|
|
|
|
cvar_t* scr_ofsy;
|
|
|
|
cvar_t* scr_ofsz;
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
cvar_t* v_centermove;
|
|
|
|
cvar_t* v_centerspeed;
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
cvar_t* cl_bobcycle;
|
|
|
|
cvar_t* cl_bob;
|
|
|
|
cvar_t* cl_bobup;
|
|
|
|
cvar_t* cl_waterdist;
|
|
|
|
cvar_t* cl_chasedist;
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
// These cvars are not registered (so users can't cheat), so set the ->value field directly
|
|
|
|
// Register these cvars in V_Init() if needed for easy tweaking
|
2021-11-28 16:54:48 +01:00
|
|
|
cvar_t v_iyaw_cycle = {"v_iyaw_cycle", "2", 0, 2};
|
|
|
|
cvar_t v_iroll_cycle = {"v_iroll_cycle", "0.5", 0, 0.5};
|
|
|
|
cvar_t v_ipitch_cycle = {"v_ipitch_cycle", "1", 0, 1};
|
|
|
|
cvar_t v_iyaw_level = {"v_iyaw_level", "0.3", 0, 0.3};
|
|
|
|
cvar_t v_iroll_level = {"v_iroll_level", "0.1", 0, 0.1};
|
|
|
|
cvar_t v_ipitch_level = {"v_ipitch_level", "0.3", 0, 0.3};
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
float v_idlescale; // used by TFC for concussion grenade effect
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
//=============================================================================
|
|
|
|
/*
|
2021-03-16 19:45:46 +01:00
|
|
|
void V_NormalizeAngles( Vector& angles )
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
// Normalize angles
|
|
|
|
for ( i = 0; i < 3; i++ )
|
|
|
|
{
|
|
|
|
if ( angles[i] > 180.0 )
|
|
|
|
{
|
|
|
|
angles[i] -= 360.0;
|
|
|
|
}
|
|
|
|
else if ( angles[i] < -180.0 )
|
|
|
|
{
|
|
|
|
angles[i] += 360.0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
===================
|
|
|
|
V_InterpolateAngles
|
|
|
|
|
|
|
|
Interpolate Euler angles.
|
|
|
|
FIXME: Use Quaternions to avoid discontinuities
|
|
|
|
Frac is 0.0 to 1.0 ( i.e., should probably be clamped, but doesn't have to be )
|
|
|
|
===================
|
|
|
|
|
|
|
|
void V_InterpolateAngles( float *start, float *end, float *output, float frac )
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
float ang1, ang2;
|
|
|
|
float d;
|
|
|
|
|
|
|
|
V_NormalizeAngles( start );
|
|
|
|
V_NormalizeAngles( end );
|
|
|
|
|
|
|
|
for ( i = 0 ; i < 3 ; i++ )
|
|
|
|
{
|
|
|
|
ang1 = start[i];
|
|
|
|
ang2 = end[i];
|
|
|
|
|
|
|
|
d = ang2 - ang1;
|
|
|
|
if ( d > 180 )
|
|
|
|
{
|
|
|
|
d -= 360;
|
|
|
|
}
|
|
|
|
else if ( d < -180 )
|
|
|
|
{
|
|
|
|
d += 360;
|
|
|
|
}
|
|
|
|
|
|
|
|
output[i] = ang1 + d * frac;
|
|
|
|
}
|
|
|
|
|
|
|
|
V_NormalizeAngles( output );
|
|
|
|
} */
|
|
|
|
|
|
|
|
// Quakeworld bob code, this fixes jitters in the mutliplayer since the clock (pparams->time) isn't quite linear
|
2021-11-28 16:54:48 +01:00
|
|
|
float V_CalcBob(struct ref_params_s* pparams)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-12-31 13:03:01 +01:00
|
|
|
static double bobtime = 0;
|
|
|
|
static float bob = 0;
|
2021-11-28 16:54:48 +01:00
|
|
|
float cycle;
|
2021-12-31 13:03:01 +01:00
|
|
|
static float lasttime = 0;
|
2021-11-28 16:54:48 +01:00
|
|
|
Vector vel;
|
|
|
|
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
if (pparams->onground == -1 ||
|
|
|
|
pparams->time == lasttime)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
// just use old value
|
2021-11-28 16:54:48 +01:00
|
|
|
return bob;
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
lasttime = pparams->time;
|
|
|
|
|
2021-12-31 13:03:01 +01:00
|
|
|
//TODO: bobtime will eventually become a value so large that it will no longer behave properly.
|
|
|
|
//Consider resetting the variable if a level change is detected (pparams->time < lasttime might do the trick).
|
2013-08-30 13:34:05 -07:00
|
|
|
bobtime += pparams->frametime;
|
2021-11-28 16:54:48 +01:00
|
|
|
cycle = bobtime - (int)(bobtime / cl_bobcycle->value) * cl_bobcycle->value;
|
2013-08-30 13:34:05 -07:00
|
|
|
cycle /= cl_bobcycle->value;
|
2021-11-28 16:54:48 +01:00
|
|
|
|
|
|
|
if (cycle < cl_bobup->value)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
cycle = M_PI * cycle / cl_bobup->value;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
cycle = M_PI + M_PI * (cycle - cl_bobup->value) / (1.0 - cl_bobup->value);
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// bob is proportional to simulated velocity in the xy plane
|
|
|
|
// (don't count Z, or jumping messes it up)
|
2021-11-28 16:54:48 +01:00
|
|
|
VectorCopy(pparams->simvel, vel);
|
2013-08-30 13:34:05 -07:00
|
|
|
vel[2] = 0;
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
bob = sqrt(vel[0] * vel[0] + vel[1] * vel[1]) * cl_bob->value;
|
2013-08-30 13:34:05 -07:00
|
|
|
bob = bob * 0.3 + bob * 0.7 * sin(cycle);
|
2024-08-28 09:56:30 +02:00
|
|
|
bob = V_min(bob, 4.0f);
|
|
|
|
bob = V_max(bob, -7.0f);
|
2013-08-30 13:34:05 -07:00
|
|
|
return bob;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
===============
|
|
|
|
V_CalcRoll
|
|
|
|
Used by view and sv_user
|
|
|
|
===============
|
|
|
|
*/
|
2021-11-28 16:54:48 +01:00
|
|
|
float V_CalcRoll(Vector angles, Vector velocity, float rollangle, float rollspeed)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
float sign;
|
|
|
|
float side;
|
|
|
|
float value;
|
|
|
|
Vector forward, right, up;
|
|
|
|
|
|
|
|
AngleVectors(angles, forward, right, up);
|
|
|
|
|
|
|
|
side = DotProduct(velocity, right);
|
|
|
|
sign = side < 0 ? -1 : 1;
|
|
|
|
side = fabs(side);
|
|
|
|
|
2013-08-30 13:34:05 -07:00
|
|
|
value = rollangle;
|
2021-11-28 16:54:48 +01:00
|
|
|
if (side < rollspeed)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
side = side * value / rollspeed;
|
|
|
|
}
|
2021-11-28 16:54:48 +01:00
|
|
|
else
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
side = value;
|
|
|
|
}
|
|
|
|
return side * sign;
|
|
|
|
}
|
|
|
|
|
|
|
|
typedef struct pitchdrift_s
|
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
float pitchvel;
|
|
|
|
int nodrift;
|
|
|
|
float driftmove;
|
|
|
|
double laststop;
|
2013-08-30 13:34:05 -07:00
|
|
|
} pitchdrift_t;
|
|
|
|
|
|
|
|
static pitchdrift_t pd;
|
|
|
|
|
2021-03-05 20:54:33 +01:00
|
|
|
void V_StartPitchDrift()
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
if (pd.laststop == gEngfuncs.GetClientTime())
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
return; // something else is keeping it from drifting
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
if (0 != pd.nodrift || 0 == pd.pitchvel)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
pd.pitchvel = v_centerspeed->value;
|
|
|
|
pd.nodrift = 0;
|
|
|
|
pd.driftmove = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
void V_StopPitchDrift()
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
pd.laststop = gEngfuncs.GetClientTime();
|
|
|
|
pd.nodrift = 1;
|
|
|
|
pd.pitchvel = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
===============
|
|
|
|
V_DriftPitch
|
|
|
|
|
|
|
|
Moves the client pitch angle towards 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 stopped.
|
|
|
|
===============
|
|
|
|
*/
|
2021-11-28 16:54:48 +01:00
|
|
|
void V_DriftPitch(struct ref_params_s* pparams)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
float delta, move;
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
if (0 != gEngfuncs.IsNoClipping() || 0 == pparams->onground || 0 != pparams->demoplayback || 0 != pparams->spectator)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
pd.driftmove = 0;
|
|
|
|
pd.pitchvel = 0;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// don't count small mouse motion
|
2021-11-28 16:54:48 +01:00
|
|
|
if (0 != pd.nodrift)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
if (v_centermove->value > 0 && (in_mlook.state & 1) == 0)
|
|
|
|
{
|
2013-08-30 13:34:05 -07:00
|
|
|
// this is for lazy players. if they stopped, looked around and then continued
|
|
|
|
// to move the view will be centered automatically if they move more than
|
2021-11-28 16:54:48 +01:00
|
|
|
// v_centermove units.
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
if (fabs(pparams->cmd->forwardmove) < cl_forwardspeed->value)
|
2013-08-30 13:34:05 -07:00
|
|
|
pd.driftmove = 0;
|
|
|
|
else
|
|
|
|
pd.driftmove += pparams->frametime;
|
2021-11-28 16:54:48 +01:00
|
|
|
|
|
|
|
if (pd.driftmove > v_centermove->value)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
V_StartPitchDrift();
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
return; // player didn't move enough
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
return; // don't drift view
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
2021-11-28 16:54:48 +01:00
|
|
|
|
2013-08-30 13:34:05 -07:00
|
|
|
delta = pparams->idealpitch - pparams->cl_viewangles[PITCH];
|
|
|
|
|
2021-11-28 15:32:26 +01:00
|
|
|
if (0 == delta)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
pd.pitchvel = 0;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
move = pparams->frametime * pd.pitchvel;
|
2021-11-28 16:54:48 +01:00
|
|
|
|
|
|
|
pd.pitchvel *= (1.0f + (pparams->frametime * 0.25f)); // get faster by time
|
|
|
|
|
2013-08-30 13:34:05 -07:00
|
|
|
if (delta > 0)
|
|
|
|
{
|
|
|
|
if (move > delta)
|
|
|
|
{
|
|
|
|
pd.pitchvel = 0;
|
|
|
|
move = delta;
|
|
|
|
}
|
|
|
|
pparams->cl_viewangles[PITCH] += move;
|
|
|
|
}
|
|
|
|
else if (delta < 0)
|
|
|
|
{
|
|
|
|
if (move > -delta)
|
|
|
|
{
|
|
|
|
pd.pitchvel = 0;
|
|
|
|
move = -delta;
|
|
|
|
}
|
|
|
|
pparams->cl_viewangles[PITCH] -= move;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
==============================================================================
|
|
|
|
VIEW RENDERING
|
|
|
|
==============================================================================
|
2021-11-28 16:54:48 +01:00
|
|
|
*/
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
/*
|
|
|
|
==================
|
|
|
|
V_CalcGunAngle
|
|
|
|
==================
|
|
|
|
*/
|
2021-11-28 16:54:48 +01:00
|
|
|
void V_CalcGunAngle(struct ref_params_s* pparams)
|
|
|
|
{
|
|
|
|
cl_entity_t* viewent;
|
|
|
|
|
2013-08-30 13:34:05 -07:00
|
|
|
viewent = gEngfuncs.GetViewModel();
|
2021-11-28 16:54:48 +01:00
|
|
|
if (!viewent)
|
2013-08-30 13:34:05 -07:00
|
|
|
return;
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
viewent->angles[YAW] = pparams->viewangles[YAW] + pparams->crosshairangle[YAW];
|
2013-08-30 13:34:05 -07:00
|
|
|
viewent->angles[PITCH] = -pparams->viewangles[PITCH] + pparams->crosshairangle[PITCH] * 0.25;
|
2021-11-28 16:54:48 +01:00
|
|
|
viewent->angles[ROLL] -= v_idlescale * sin(pparams->time * v_iroll_cycle.value) * v_iroll_level.value;
|
|
|
|
|
2013-08-30 13:34:05 -07:00
|
|
|
// don't apply all of the v_ipitch to prevent normally unseen parts of viewmodel from coming into view.
|
2021-11-28 16:54:48 +01:00
|
|
|
viewent->angles[PITCH] -= v_idlescale * sin(pparams->time * v_ipitch_cycle.value) * (v_ipitch_level.value * 0.5);
|
|
|
|
viewent->angles[YAW] -= v_idlescale * sin(pparams->time * v_iyaw_cycle.value) * v_iyaw_level.value;
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
==============
|
|
|
|
V_AddIdle
|
|
|
|
|
|
|
|
Idle swaying
|
|
|
|
==============
|
|
|
|
*/
|
2021-11-28 16:54:48 +01:00
|
|
|
void V_AddIdle(struct ref_params_s* pparams)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
pparams->viewangles[ROLL] += v_idlescale * sin(pparams->time * v_iroll_cycle.value) * v_iroll_level.value;
|
|
|
|
pparams->viewangles[PITCH] += v_idlescale * sin(pparams->time * v_ipitch_cycle.value) * v_ipitch_level.value;
|
|
|
|
pparams->viewangles[YAW] += v_idlescale * sin(pparams->time * v_iyaw_cycle.value) * v_iyaw_level.value;
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
|
2013-08-30 13:34:05 -07:00
|
|
|
/*
|
|
|
|
==============
|
|
|
|
V_CalcViewRoll
|
|
|
|
|
|
|
|
Roll is induced by movement and damage
|
|
|
|
==============
|
|
|
|
*/
|
2021-11-28 16:54:48 +01:00
|
|
|
void V_CalcViewRoll(struct ref_params_s* pparams)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
float side;
|
|
|
|
cl_entity_t* viewentity;
|
|
|
|
|
|
|
|
viewentity = gEngfuncs.GetEntityByIndex(pparams->viewentity);
|
|
|
|
if (!viewentity)
|
2013-08-30 13:34:05 -07:00
|
|
|
return;
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
side = V_CalcRoll(viewentity->angles, pparams->simvel, cl_rollangle->value, cl_rollspeed->value);
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
pparams->viewangles[ROLL] += side;
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
if (pparams->health <= 0 && (pparams->viewheight[2] != 0))
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
// only roll the view if the player is dead and the viewheight[2] is nonzero
|
2013-08-30 13:34:05 -07:00
|
|
|
// this is so deadcam in multiplayer will work.
|
2021-11-28 16:54:48 +01:00
|
|
|
pparams->viewangles[ROLL] = 80; // dead view angle
|
2013-08-30 13:34:05 -07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
==================
|
|
|
|
V_CalcIntermissionRefdef
|
|
|
|
|
|
|
|
==================
|
|
|
|
*/
|
2021-11-28 16:54:48 +01:00
|
|
|
void V_CalcIntermissionRefdef(struct ref_params_s* pparams)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
cl_entity_t *ent, *view;
|
|
|
|
float old;
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
// ent is the player model ( visible when out of body )
|
|
|
|
ent = gEngfuncs.GetLocalPlayer();
|
2021-11-28 16:54:48 +01:00
|
|
|
|
2013-08-30 13:34:05 -07:00
|
|
|
// view is the weapon model (only visible from inside body )
|
|
|
|
view = gEngfuncs.GetViewModel();
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
VectorCopy(pparams->simorg, pparams->vieworg);
|
|
|
|
VectorCopy(pparams->cl_viewangles, pparams->viewangles);
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
view->model = NULL;
|
|
|
|
|
|
|
|
// allways idle in intermission
|
|
|
|
old = v_idlescale;
|
|
|
|
v_idlescale = 1;
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
V_AddIdle(pparams);
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
if (0 != gEngfuncs.IsSpectateOnly())
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
// in HLTV we must go to 'intermission' position by ourself
|
2021-11-28 16:54:48 +01:00
|
|
|
VectorCopy(gHUD.m_Spectator.m_cameraOrigin, pparams->vieworg);
|
|
|
|
VectorCopy(gHUD.m_Spectator.m_cameraAngles, pparams->viewangles);
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
v_idlescale = old;
|
|
|
|
|
|
|
|
v_cl_angles = pparams->cl_viewangles;
|
|
|
|
v_origin = pparams->vieworg;
|
|
|
|
v_angles = pparams->viewangles;
|
|
|
|
}
|
|
|
|
|
|
|
|
#define ORIGIN_BACKUP 64
|
2021-11-28 16:54:48 +01:00
|
|
|
#define ORIGIN_MASK (ORIGIN_BACKUP - 1)
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
typedef struct
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
float Origins[ORIGIN_BACKUP][3];
|
|
|
|
float OriginTime[ORIGIN_BACKUP];
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
float Angles[ORIGIN_BACKUP][3];
|
|
|
|
float AngleTime[ORIGIN_BACKUP];
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
int CurrentOrigin;
|
|
|
|
int CurrentAngle;
|
|
|
|
} viewinterp_t;
|
|
|
|
|
|
|
|
/*
|
|
|
|
==================
|
|
|
|
V_CalcRefdef
|
|
|
|
|
|
|
|
==================
|
|
|
|
*/
|
2021-11-28 16:54:48 +01:00
|
|
|
void V_CalcNormalRefdef(struct ref_params_s* pparams)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
cl_entity_t *ent, *view;
|
|
|
|
int i;
|
|
|
|
Vector angles;
|
|
|
|
float bob, waterOffset;
|
|
|
|
static viewinterp_t ViewInterp;
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
static float oldz = 0;
|
|
|
|
static float lasttime;
|
|
|
|
|
2021-03-16 19:45:46 +01:00
|
|
|
Vector camAngles, camForward, camRight, camUp;
|
2021-11-28 16:54:48 +01:00
|
|
|
cl_entity_t* pwater;
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
V_DriftPitch(pparams);
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
if (0 != gEngfuncs.IsSpectateOnly())
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
ent = gEngfuncs.GetEntityByIndex(g_iUser2);
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// ent is the player model ( visible when out of body )
|
|
|
|
ent = gEngfuncs.GetLocalPlayer();
|
|
|
|
}
|
2021-11-28 16:54:48 +01:00
|
|
|
|
2013-08-30 13:34:05 -07:00
|
|
|
// view is the weapon model (only visible from inside body )
|
|
|
|
view = gEngfuncs.GetViewModel();
|
|
|
|
|
|
|
|
// transform the view offset by the model's matrix to get the offset from
|
|
|
|
// model origin for the view
|
2021-11-28 16:54:48 +01:00
|
|
|
bob = V_CalcBob(pparams);
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
// refresh position
|
2021-11-28 16:54:48 +01:00
|
|
|
VectorCopy(pparams->simorg, pparams->vieworg);
|
|
|
|
pparams->vieworg[2] += (bob);
|
|
|
|
VectorAdd(pparams->vieworg, pparams->viewheight, pparams->vieworg);
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
VectorCopy(pparams->cl_viewangles, pparams->viewangles);
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
gEngfuncs.V_CalcShake();
|
2021-11-28 16:54:48 +01:00
|
|
|
gEngfuncs.V_ApplyShake(pparams->vieworg, pparams->viewangles, 1.0);
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
// never let view origin sit exactly on a node line, because a water plane can
|
|
|
|
// dissapear when viewed with the eye exactly on it.
|
|
|
|
// FIXME, we send origin at 1/128 now, change this?
|
|
|
|
// the server protocol only specifies to 1/16 pixel, so add 1/32 in each axis
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
pparams->vieworg[0] += 1.0 / 32;
|
|
|
|
pparams->vieworg[1] += 1.0 / 32;
|
|
|
|
pparams->vieworg[2] += 1.0 / 32;
|
|
|
|
|
|
|
|
// Check for problems around water, move the viewer artificially if necessary
|
2013-08-30 13:34:05 -07:00
|
|
|
// -- this prevents drawing errors in GL due to waves
|
|
|
|
|
|
|
|
waterOffset = 0;
|
2021-11-28 16:54:48 +01:00
|
|
|
if (pparams->waterlevel >= 2)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
int i, contents, waterDist, waterEntity;
|
|
|
|
Vector point;
|
2013-08-30 13:34:05 -07:00
|
|
|
waterDist = cl_waterdist->value;
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
if (0 != pparams->hardware)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
waterEntity = gEngfuncs.PM_WaterEntity(pparams->simorg);
|
|
|
|
if (waterEntity >= 0 && waterEntity < pparams->max_entities)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
pwater = gEngfuncs.GetEntityByIndex(waterEntity);
|
|
|
|
if (pwater && (pwater->model != NULL))
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
waterDist += (pwater->curstate.scale * 16); // Add in wave height
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
waterEntity = 0; // Don't need this in software
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
2021-11-28 16:54:48 +01:00
|
|
|
|
|
|
|
VectorCopy(pparams->vieworg, point);
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
// Eyes are above water, make sure we're above the waves
|
2021-11-28 16:54:48 +01:00
|
|
|
if (pparams->waterlevel == 2)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
point[2] -= waterDist;
|
2021-11-28 16:54:48 +01:00
|
|
|
for (i = 0; i < waterDist; i++)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
contents = gEngfuncs.PM_PointContents(point, NULL);
|
|
|
|
if (contents > CONTENTS_WATER)
|
2013-08-30 13:34:05 -07:00
|
|
|
break;
|
|
|
|
point[2] += 1;
|
|
|
|
}
|
|
|
|
waterOffset = (point[2] + waterDist) - pparams->vieworg[2];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// eyes are under water. Make sure we're far enough under
|
|
|
|
point[2] += waterDist;
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
for (i = 0; i < waterDist; i++)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
contents = gEngfuncs.PM_PointContents(point, NULL);
|
|
|
|
if (contents <= CONTENTS_WATER)
|
2013-08-30 13:34:05 -07:00
|
|
|
break;
|
|
|
|
point[2] -= 1;
|
|
|
|
}
|
|
|
|
waterOffset = (point[2] - waterDist) - pparams->vieworg[2];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pparams->vieworg[2] += waterOffset;
|
2021-11-28 16:54:48 +01:00
|
|
|
|
|
|
|
V_CalcViewRoll(pparams);
|
|
|
|
|
|
|
|
V_AddIdle(pparams);
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
// offsets
|
2021-11-28 16:54:48 +01:00
|
|
|
VectorCopy(pparams->cl_viewangles, angles);
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
AngleVectors(angles, pparams->forward, pparams->right, pparams->up);
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
// don't allow cheats in multiplayer
|
2021-11-28 16:54:48 +01:00
|
|
|
if (pparams->maxclients <= 1)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
for (i = 0; i < 3; i++)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
pparams->vieworg[i] += scr_ofsx->value * pparams->forward[i] + scr_ofsy->value * pparams->right[i] + scr_ofsz->value * pparams->up[i];
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
}
|
2021-11-28 16:54:48 +01:00
|
|
|
|
2013-08-30 13:34:05 -07:00
|
|
|
// Treating cam_ofs[2] as the distance
|
2021-11-28 16:54:48 +01:00
|
|
|
if (0 != CL_IsThirdPerson())
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-03-16 19:45:46 +01:00
|
|
|
Vector ofs;
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
ofs[0] = ofs[1] = ofs[2] = 0.0;
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
CL_CameraOffset((float*)&ofs);
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
VectorCopy(ofs, camAngles);
|
|
|
|
camAngles[ROLL] = 0;
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
AngleVectors(camAngles, camForward, camRight, camUp);
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
for (i = 0; i < 3; i++)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
pparams->vieworg[i] += -ofs[2] * camForward[i];
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
}
|
2021-11-28 16:54:48 +01:00
|
|
|
|
2013-08-30 13:34:05 -07:00
|
|
|
// Give gun our viewangles
|
2021-11-28 16:54:48 +01:00
|
|
|
VectorCopy(pparams->cl_viewangles, view->angles);
|
|
|
|
|
2013-08-30 13:34:05 -07:00
|
|
|
// set up gun position
|
2021-11-28 16:54:48 +01:00
|
|
|
V_CalcGunAngle(pparams);
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
// Use predicted origin as view origin.
|
2021-11-28 16:54:48 +01:00
|
|
|
VectorCopy(pparams->simorg, view->origin);
|
|
|
|
view->origin[2] += (waterOffset);
|
|
|
|
VectorAdd(view->origin, pparams->viewheight, view->origin);
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
// Let the viewmodel shake at about 10% of the amplitude
|
2021-11-28 16:54:48 +01:00
|
|
|
gEngfuncs.V_ApplyShake(view->origin, view->angles, 0.9);
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
for (i = 0; i < 3; i++)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
view->origin[i] += bob * 0.4 * pparams->forward[i];
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
view->origin[2] += bob;
|
|
|
|
|
|
|
|
// throw in a little tilt.
|
2021-11-28 16:54:48 +01:00
|
|
|
view->angles[YAW] -= bob * 0.5;
|
|
|
|
view->angles[ROLL] -= bob * 1;
|
2013-08-30 13:34:05 -07:00
|
|
|
view->angles[PITCH] -= bob * 0.3;
|
|
|
|
|
2021-11-28 15:32:26 +01:00
|
|
|
if (0 != cl_bobtilt->value)
|
2021-02-08 12:41:02 +01:00
|
|
|
{
|
|
|
|
VectorCopy(view->angles, view->curstate.angles);
|
|
|
|
}
|
|
|
|
|
2013-08-30 13:34:05 -07:00
|
|
|
// pushing the view origin down off of the same X/Z plane as the ent's origin will give the
|
|
|
|
// gun a very nice 'shifting' effect when the player looks up/down. If there is a problem
|
2021-11-28 16:54:48 +01:00
|
|
|
// with view model distortion, this may be a cause. (SJB).
|
2013-08-30 13:34:05 -07:00
|
|
|
view->origin[2] -= 1;
|
|
|
|
|
|
|
|
// fudge position around to keep amount of weapon visible
|
|
|
|
// roughly equal with different FOV
|
|
|
|
if (pparams->viewsize == 110)
|
|
|
|
{
|
|
|
|
view->origin[2] += 1;
|
|
|
|
}
|
|
|
|
else if (pparams->viewsize == 100)
|
|
|
|
{
|
|
|
|
view->origin[2] += 2;
|
|
|
|
}
|
|
|
|
else if (pparams->viewsize == 90)
|
|
|
|
{
|
|
|
|
view->origin[2] += 1;
|
|
|
|
}
|
|
|
|
else if (pparams->viewsize == 80)
|
|
|
|
{
|
|
|
|
view->origin[2] += 0.5;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add in the punchangle, if any
|
2021-11-28 16:54:48 +01:00
|
|
|
VectorAdd(pparams->viewangles, pparams->punchangle, pparams->viewangles);
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
// Include client side punch, too
|
2021-11-28 16:54:48 +01:00
|
|
|
VectorAdd(pparams->viewangles, (float*)&ev_punchangle, pparams->viewangles);
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
V_DropPunchAngle(pparams->frametime, (float*)&ev_punchangle);
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
// smooth out stair step ups
|
|
|
|
#if 1
|
2021-11-28 16:54:48 +01:00
|
|
|
if (0 == pparams->smoothing && 0 != pparams->onground && pparams->simorg[2] - oldz > 0)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
float steptime;
|
2021-11-28 16:54:48 +01:00
|
|
|
|
2013-08-30 13:34:05 -07:00
|
|
|
steptime = pparams->time - lasttime;
|
|
|
|
if (steptime < 0)
|
2021-11-28 16:54:48 +01:00
|
|
|
//FIXME I_Error ("steptime < 0");
|
2013-08-30 13:34:05 -07:00
|
|
|
steptime = 0;
|
|
|
|
|
|
|
|
oldz += steptime * 150;
|
|
|
|
if (oldz > pparams->simorg[2])
|
|
|
|
oldz = pparams->simorg[2];
|
|
|
|
if (pparams->simorg[2] - oldz > 18)
|
2021-11-28 16:54:48 +01:00
|
|
|
oldz = pparams->simorg[2] - 18;
|
2013-08-30 13:34:05 -07:00
|
|
|
pparams->vieworg[2] += oldz - pparams->simorg[2];
|
|
|
|
view->origin[2] += oldz - pparams->simorg[2];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
oldz = pparams->simorg[2];
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
{
|
|
|
|
static float lastorg[3];
|
2021-03-16 19:45:46 +01:00
|
|
|
Vector delta;
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
VectorSubtract(pparams->simorg, lastorg, delta);
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
if (Length(delta) != 0.0)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
VectorCopy(pparams->simorg, ViewInterp.Origins[ViewInterp.CurrentOrigin & ORIGIN_MASK]);
|
|
|
|
ViewInterp.OriginTime[ViewInterp.CurrentOrigin & ORIGIN_MASK] = pparams->time;
|
2013-08-30 13:34:05 -07:00
|
|
|
ViewInterp.CurrentOrigin++;
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
VectorCopy(pparams->simorg, lastorg);
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Smooth out whole view in multiplayer when on trains, lifts
|
2021-11-28 16:54:48 +01:00
|
|
|
if (cl_vsmoothing && 0 != cl_vsmoothing->value &&
|
|
|
|
(0 != pparams->smoothing && (pparams->maxclients > 1)))
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
int foundidx;
|
|
|
|
int i;
|
|
|
|
float t;
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
if (cl_vsmoothing->value < 0.0)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
gEngfuncs.Cvar_SetValue("cl_vsmoothing", 0.0);
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
t = pparams->time - cl_vsmoothing->value;
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
for (i = 1; i < ORIGIN_MASK; i++)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
foundidx = ViewInterp.CurrentOrigin - 1 - i;
|
2021-11-28 16:54:48 +01:00
|
|
|
if (ViewInterp.OriginTime[foundidx & ORIGIN_MASK] <= t)
|
2013-08-30 13:34:05 -07:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
if (i < ORIGIN_MASK && ViewInterp.OriginTime[foundidx & ORIGIN_MASK] != 0.0)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
// Interpolate
|
2021-03-16 19:45:46 +01:00
|
|
|
Vector delta;
|
2013-08-30 13:34:05 -07:00
|
|
|
double frac;
|
|
|
|
double dt;
|
2021-03-16 19:45:46 +01:00
|
|
|
Vector neworg;
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
dt = ViewInterp.OriginTime[(foundidx + 1) & ORIGIN_MASK] - ViewInterp.OriginTime[foundidx & ORIGIN_MASK];
|
|
|
|
if (dt > 0.0)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
frac = (t - ViewInterp.OriginTime[foundidx & ORIGIN_MASK]) / dt;
|
|
|
|
frac = V_min(1.0, frac);
|
|
|
|
VectorSubtract(ViewInterp.Origins[(foundidx + 1) & ORIGIN_MASK], ViewInterp.Origins[foundidx & ORIGIN_MASK], delta);
|
|
|
|
VectorMA(ViewInterp.Origins[foundidx & ORIGIN_MASK], frac, delta, neworg);
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
// Dont interpolate large changes
|
2021-11-28 16:54:48 +01:00
|
|
|
if (Length(delta) < 64)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
VectorSubtract(neworg, pparams->simorg, delta);
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
VectorAdd(pparams->simorg, delta, pparams->simorg);
|
|
|
|
VectorAdd(pparams->vieworg, delta, pparams->vieworg);
|
|
|
|
VectorAdd(view->origin, delta, view->origin);
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Store off v_angles before munging for third person
|
|
|
|
v_angles = pparams->viewangles;
|
2021-03-13 11:42:43 +01:00
|
|
|
v_client_aimangles = pparams->cl_viewangles;
|
2013-08-30 13:34:05 -07:00
|
|
|
v_lastAngles = pparams->viewangles;
|
2021-11-28 16:54:48 +01:00
|
|
|
// v_cl_angles = pparams->cl_viewangles; // keep old user mouse angles !
|
|
|
|
if (0 != CL_IsThirdPerson())
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
VectorCopy(camAngles, pparams->viewangles);
|
2021-03-13 15:53:48 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
//Apply this at all times
|
|
|
|
{
|
|
|
|
float pitch = pparams->viewangles[0];
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
// Normalize angles
|
2021-03-13 15:53:48 +01:00
|
|
|
if (pitch > 180)
|
2013-08-30 13:34:05 -07:00
|
|
|
pitch -= 360.0;
|
2021-03-13 15:53:48 +01:00
|
|
|
else if (pitch < -180)
|
2013-08-30 13:34:05 -07:00
|
|
|
pitch += 360;
|
|
|
|
|
|
|
|
// Player pitch is inverted
|
|
|
|
pitch /= -3.0;
|
|
|
|
|
|
|
|
// Slam local player's pitch value
|
2021-03-13 15:53:48 +01:00
|
|
|
ent->angles[0] = pitch;
|
|
|
|
ent->curstate.angles[0] = pitch;
|
|
|
|
ent->prevstate.angles[0] = pitch;
|
|
|
|
ent->latched.prevangles[0] = pitch;
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// override all previous settings if the viewent isn't the client
|
2021-11-28 16:54:48 +01:00
|
|
|
if (pparams->viewentity > pparams->maxclients)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
cl_entity_t* viewentity;
|
|
|
|
viewentity = gEngfuncs.GetEntityByIndex(pparams->viewentity);
|
|
|
|
if (viewentity)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
VectorCopy(viewentity->origin, pparams->vieworg);
|
|
|
|
VectorCopy(viewentity->angles, pparams->viewangles);
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
// Store off overridden viewangles
|
|
|
|
v_angles = pparams->viewangles;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-08-28 09:56:30 +02:00
|
|
|
// Update the latched view origin/angles here, this was
|
|
|
|
// previously done in V_CalcGunAngle but that happens
|
|
|
|
// before a bunch of other stuff happens, which nukes
|
|
|
|
// a bunch of the viewbob fx.
|
|
|
|
VectorCopy(view->origin, view->curstate.origin);
|
|
|
|
VectorCopy(view->origin, view->latched.prevorigin);
|
|
|
|
VectorCopy(view->angles, view->curstate.angles);
|
|
|
|
VectorCopy(view->angles, view->latched.prevangles);
|
|
|
|
|
2013-08-30 13:34:05 -07:00
|
|
|
lasttime = pparams->time;
|
|
|
|
|
|
|
|
v_origin = pparams->vieworg;
|
|
|
|
}
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
void V_SmoothInterpolateAngles(float* startAngle, float* endAngle, float* finalAngle, float degreesPerSec)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
float absd, frac, d, threshhold;
|
|
|
|
|
|
|
|
NormalizeAngles(startAngle);
|
|
|
|
NormalizeAngles(endAngle);
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
for (int i = 0; i < 3; i++)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
d = endAngle[i] - startAngle[i];
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
if (d > 180.0f)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
d -= 360.0f;
|
|
|
|
}
|
2021-11-28 16:54:48 +01:00
|
|
|
else if (d < -180.0f)
|
|
|
|
{
|
2013-08-30 13:34:05 -07:00
|
|
|
d += 360.0f;
|
|
|
|
}
|
|
|
|
|
|
|
|
absd = fabs(d);
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
if (absd > 0.01f)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
frac = degreesPerSec * v_frametime;
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
threshhold = degreesPerSec / 4;
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
if (absd < threshhold)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
float h = absd / threshhold;
|
|
|
|
h *= h;
|
2021-11-28 16:54:48 +01:00
|
|
|
frac *= h; // slow down last degrees
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
if (frac > absd)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
finalAngle[i] = endAngle[i];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
if (d > 0)
|
2013-08-30 13:34:05 -07:00
|
|
|
finalAngle[i] = startAngle[i] + frac;
|
|
|
|
else
|
|
|
|
finalAngle[i] = startAngle[i] - frac;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
finalAngle[i] = endAngle[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
NormalizeAngles(finalAngle);
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// Get the origin of the Observer based around the target's position and angles
|
2021-11-28 16:54:48 +01:00
|
|
|
void V_GetChaseOrigin(float* angles, float* origin, float distance, float* returnvec)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
Vector vecEnd;
|
|
|
|
Vector forward;
|
|
|
|
Vector vecStart;
|
|
|
|
pmtrace_t* trace;
|
2013-08-30 13:34:05 -07:00
|
|
|
int maxLoops = 8;
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
int ignoreent = -1; // first, ignore no entity
|
|
|
|
|
|
|
|
cl_entity_t* ent = NULL;
|
|
|
|
|
2013-08-30 13:34:05 -07:00
|
|
|
// Trace back from the target using the player's view angles
|
|
|
|
AngleVectors(angles, forward, NULL, NULL);
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
VectorScale(forward, -1, forward);
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
VectorCopy(origin, vecStart);
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
VectorMA(vecStart, distance, forward, vecEnd);
|
|
|
|
|
|
|
|
while (maxLoops > 0)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
trace = gEngfuncs.PM_TraceLine(vecStart, vecEnd, PM_TRACELINE_PHYSENTSONLY, 2, ignoreent);
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
// WARNING! trace->ent is is the number in physent list not the normal entity number
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
if (trace->ent <= 0)
|
|
|
|
break; // we hit the world or nothing, stop trace
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
ent = gEngfuncs.GetEntityByIndex(PM_GetPhysEntInfo(trace->ent));
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
if (ent == NULL)
|
2013-08-30 13:34:05 -07:00
|
|
|
break;
|
|
|
|
|
|
|
|
// hit non-player solid BSP , stop here
|
2021-11-28 16:54:48 +01:00
|
|
|
if (ent->curstate.solid == SOLID_BSP && 0 == ent->player)
|
2013-08-30 13:34:05 -07:00
|
|
|
break;
|
|
|
|
|
|
|
|
// if close enought to end pos, stop, otherwise continue trace
|
2021-11-28 16:54:48 +01:00
|
|
|
if (Distance(trace->endpos, vecEnd) < 1.0f)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
ignoreent = trace->ent; // ignore last hit entity
|
|
|
|
VectorCopy(trace->endpos, vecStart);
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
maxLoops--;
|
2021-11-28 16:54:48 +01:00
|
|
|
}
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
/* if ( ent )
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
gEngfuncs.Con_Printf("Trace loops %i , entity %i, model %s, solid %i\n",(8-maxLoops),ent->curstate.number, ent->model->name , ent->curstate.solid );
|
|
|
|
} */
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
VectorMA(trace->endpos, 4, trace->plane.normal, returnvec);
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
v_lastDistance = Distance(trace->endpos, origin); // real distance without offset
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/*void V_GetDeathCam(cl_entity_t * ent1, cl_entity_t * ent2, float * angle, float * origin)
|
|
|
|
{
|
|
|
|
float newAngle[3]; float newOrigin[3];
|
|
|
|
|
|
|
|
float distance = 168.0f;
|
|
|
|
|
|
|
|
v_lastDistance+= v_frametime * 96.0f; // move unit per seconds back
|
|
|
|
|
|
|
|
if ( v_resetCamera )
|
|
|
|
v_lastDistance = 64.0f;
|
|
|
|
|
|
|
|
if ( distance > v_lastDistance )
|
|
|
|
distance = v_lastDistance;
|
|
|
|
|
|
|
|
VectorCopy(ent1->origin, newOrigin);
|
|
|
|
|
|
|
|
if ( ent1->player )
|
|
|
|
newOrigin[2]+= 17; // head level of living player
|
|
|
|
|
|
|
|
// get new angle towards second target
|
|
|
|
if ( ent2 )
|
|
|
|
{
|
|
|
|
VectorSubtract( ent2->origin, ent1->origin, newAngle );
|
|
|
|
VectorAngles( newAngle, newAngle );
|
|
|
|
newAngle[0] = -newAngle[0];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// if no second target is given, look down to dead player
|
|
|
|
newAngle[0] = 90.0f;
|
|
|
|
newAngle[1] = 0.0f;
|
|
|
|
newAngle[2] = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// and smooth view
|
|
|
|
V_SmoothInterpolateAngles( v_lastAngles, newAngle, angle, 120.0f );
|
|
|
|
|
|
|
|
V_GetChaseOrigin( angle, newOrigin, distance, origin );
|
|
|
|
|
|
|
|
VectorCopy(angle, v_lastAngles);
|
|
|
|
}*/
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
void V_GetSingleTargetCam(cl_entity_t* ent1, float* angle, float* origin)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
float newAngle[3];
|
|
|
|
float newOrigin[3];
|
|
|
|
|
|
|
|
int flags = gHUD.m_Spectator.m_iObserverFlags;
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
// see is target is a dead player
|
2021-11-28 15:32:26 +01:00
|
|
|
bool deadPlayer = 0 != ent1->player && (ent1->curstate.solid == SOLID_NOT);
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
float dfactor = (flags & DRC_FLAG_DRAMATIC) != 0 ? -1.0f : 1.0f;
|
|
|
|
|
|
|
|
float distance = 112.0f + (16.0f * dfactor); // get close if dramatic;
|
|
|
|
|
2013-08-30 13:34:05 -07:00
|
|
|
// go away in final scenes or if player just died
|
2021-11-28 16:54:48 +01:00
|
|
|
if ((flags & DRC_FLAG_FINAL) != 0)
|
|
|
|
distance *= 2.0f;
|
|
|
|
else if (deadPlayer)
|
|
|
|
distance *= 1.5f;
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
// let v_lastDistance float smoothly away
|
2021-11-28 16:54:48 +01:00
|
|
|
v_lastDistance += v_frametime * 32.0f; // move unit per seconds back
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
if (distance > v_lastDistance)
|
2013-08-30 13:34:05 -07:00
|
|
|
distance = v_lastDistance;
|
2021-11-28 16:54:48 +01:00
|
|
|
|
2013-08-30 13:34:05 -07:00
|
|
|
VectorCopy(ent1->origin, newOrigin);
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
if (0 != ent1->player)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
if (deadPlayer)
|
|
|
|
newOrigin[2] += 2; //laying on ground
|
2013-08-30 13:34:05 -07:00
|
|
|
else
|
2021-11-28 16:54:48 +01:00
|
|
|
newOrigin[2] += 17; // head level of living player
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
else
|
2021-11-28 16:54:48 +01:00
|
|
|
newOrigin[2] += 8; // object, tricky, must be above bomb in CS
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
// we have no second target, choose view direction based on
|
|
|
|
// show front of primary target
|
|
|
|
VectorCopy(ent1->angles, newAngle);
|
|
|
|
|
|
|
|
// show dead players from front, normal players back
|
2021-11-28 16:54:48 +01:00
|
|
|
if ((flags & DRC_FLAG_FACEPLAYER) != 0)
|
|
|
|
newAngle[1] += 180.0f;
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
newAngle[0] += 12.5f * dfactor; // lower angle if dramatic
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
// if final scene (bomb), show from real high pos
|
2021-11-28 16:54:48 +01:00
|
|
|
if ((flags & DRC_FLAG_FINAL) != 0)
|
|
|
|
newAngle[0] = 22.5f;
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
// choose side of object/player
|
|
|
|
if ((flags & DRC_FLAG_SIDE) != 0)
|
|
|
|
newAngle[1] += 22.5f;
|
2013-08-30 13:34:05 -07:00
|
|
|
else
|
2021-11-28 16:54:48 +01:00
|
|
|
newAngle[1] -= 22.5f;
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
V_SmoothInterpolateAngles(v_lastAngles, newAngle, angle, 120.0f);
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
// HACK, if player is dead don't clip against his dead body, can't check this
|
2021-11-28 16:54:48 +01:00
|
|
|
V_GetChaseOrigin(angle, newOrigin, distance, origin);
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
float MaxAngleBetweenAngles(float* a1, float* a2)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
float d, maxd = 0.0f;
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
NormalizeAngles(a1);
|
|
|
|
NormalizeAngles(a2);
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
for (int i = 0; i < 3; i++)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
d = a2[i] - a1[i];
|
2021-11-28 16:54:48 +01:00
|
|
|
if (d > 180)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
d -= 360;
|
|
|
|
}
|
2021-11-28 16:54:48 +01:00
|
|
|
else if (d < -180)
|
|
|
|
{
|
2013-08-30 13:34:05 -07:00
|
|
|
d += 360;
|
|
|
|
}
|
|
|
|
|
|
|
|
d = fabs(d);
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
if (d > maxd)
|
|
|
|
maxd = d;
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
return maxd;
|
|
|
|
}
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
void V_GetDoubleTargetsCam(cl_entity_t* ent1, cl_entity_t* ent2, float* angle, float* origin)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
float newAngle[3];
|
|
|
|
float newOrigin[3];
|
|
|
|
float tempVec[3];
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
int flags = gHUD.m_Spectator.m_iObserverFlags;
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
float dfactor = (flags & DRC_FLAG_DRAMATIC) != 0 ? -1.0f : 1.0f;
|
|
|
|
|
|
|
|
float distance = 112.0f + (16.0f * dfactor); // get close if dramatic;
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
// go away in final scenes or if player just died
|
2021-11-28 16:54:48 +01:00
|
|
|
if ((flags & DRC_FLAG_FINAL) != 0)
|
|
|
|
distance *= 2.0f;
|
|
|
|
|
2013-08-30 13:34:05 -07:00
|
|
|
// let v_lastDistance float smoothly away
|
2021-11-28 16:54:48 +01:00
|
|
|
v_lastDistance += v_frametime * 32.0f; // move unit per seconds back
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
if (distance > v_lastDistance)
|
2013-08-30 13:34:05 -07:00
|
|
|
distance = v_lastDistance;
|
|
|
|
|
|
|
|
VectorCopy(ent1->origin, newOrigin);
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
if (0 != ent1->player)
|
|
|
|
newOrigin[2] += 17; // head level of living player
|
2013-08-30 13:34:05 -07:00
|
|
|
else
|
2021-11-28 16:54:48 +01:00
|
|
|
newOrigin[2] += 8; // object, tricky, must be above bomb in CS
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
// get new angle towards second target
|
2021-11-28 16:54:48 +01:00
|
|
|
VectorSubtract(ent2->origin, ent1->origin, newAngle);
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
VectorAngles(newAngle, newAngle);
|
2013-08-30 13:34:05 -07:00
|
|
|
newAngle[0] = -newAngle[0];
|
|
|
|
|
|
|
|
// set angle diffrent in Dramtaic scenes
|
2021-11-28 16:54:48 +01:00
|
|
|
newAngle[0] += 12.5f * dfactor; // lower angle if dramatic
|
|
|
|
|
|
|
|
if ((flags & DRC_FLAG_SIDE) != 0)
|
|
|
|
newAngle[1] += 22.5f;
|
2013-08-30 13:34:05 -07:00
|
|
|
else
|
2021-11-28 16:54:48 +01:00
|
|
|
newAngle[1] -= 22.5f;
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
float d = MaxAngleBetweenAngles(v_lastAngles, newAngle);
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
if ((d < v_cameraFocusAngle) && (v_cameraMode == CAM_MODE_RELAX))
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
// difference is to small and we are in relax camera mode, keep viewangles
|
2021-11-28 16:54:48 +01:00
|
|
|
VectorCopy(v_lastAngles, newAngle);
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
2021-11-28 16:54:48 +01:00
|
|
|
else if ((d < v_cameraRelaxAngle) && (v_cameraMode == CAM_MODE_FOCUS))
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
// we catched up with our target, relax again
|
|
|
|
v_cameraMode = CAM_MODE_RELAX;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// target move too far away, focus camera again
|
|
|
|
v_cameraMode = CAM_MODE_FOCUS;
|
|
|
|
}
|
|
|
|
|
|
|
|
// and smooth view, if not a scene cut
|
2021-11-28 16:54:48 +01:00
|
|
|
if (v_resetCamera || (v_cameraMode == CAM_MODE_RELAX))
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
VectorCopy(newAngle, angle);
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
V_SmoothInterpolateAngles(v_lastAngles, newAngle, angle, 180.0f);
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
V_GetChaseOrigin(newAngle, newOrigin, distance, origin);
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
// move position up, if very close at target
|
2021-11-28 16:54:48 +01:00
|
|
|
if (v_lastDistance < 64.0f)
|
|
|
|
origin[2] += 16.0f * (1.0f - (v_lastDistance / 64.0f));
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
// calculate angle to second target
|
2021-11-28 16:54:48 +01:00
|
|
|
VectorSubtract(ent2->origin, origin, tempVec);
|
|
|
|
VectorAngles(tempVec, tempVec);
|
2013-08-30 13:34:05 -07:00
|
|
|
tempVec[0] = -tempVec[0];
|
|
|
|
|
|
|
|
/* take middle between two viewangles
|
|
|
|
InterpolateAngles( newAngle, tempVec, newAngle, 0.5f); */
|
|
|
|
}
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
void V_GetDirectedChasePosition(cl_entity_t* ent1, cl_entity_t* ent2, float* angle, float* origin)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
if (v_resetCamera)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
v_lastDistance = 4096.0f;
|
|
|
|
// v_cameraMode = CAM_MODE_FOCUS;
|
|
|
|
}
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
if ((ent2 == (cl_entity_t*)0xFFFFFFFF) || (0 != ent1->player && (ent1->curstate.solid == SOLID_NOT)))
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
// we have no second target or player just died
|
|
|
|
V_GetSingleTargetCam(ent1, angle, origin);
|
|
|
|
}
|
2021-11-28 16:54:48 +01:00
|
|
|
else if (ent2)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
// keep both target in view
|
2021-11-28 16:54:48 +01:00
|
|
|
V_GetDoubleTargetsCam(ent1, ent2, angle, origin);
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// second target disappeard somehow (dead)
|
|
|
|
|
|
|
|
// keep last good viewangle
|
|
|
|
float newOrigin[3];
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
int flags = gHUD.m_Spectator.m_iObserverFlags;
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
float dfactor = (flags & DRC_FLAG_DRAMATIC) != 0 ? -1.0f : 1.0f;
|
|
|
|
|
|
|
|
float distance = 112.0f + (16.0f * dfactor); // get close if dramatic;
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
// go away in final scenes or if player just died
|
2021-11-28 16:54:48 +01:00
|
|
|
if ((flags & DRC_FLAG_FINAL) != 0)
|
|
|
|
distance *= 2.0f;
|
|
|
|
|
2013-08-30 13:34:05 -07:00
|
|
|
// let v_lastDistance float smoothly away
|
2021-11-28 16:54:48 +01:00
|
|
|
v_lastDistance += v_frametime * 32.0f; // move unit per seconds back
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
if (distance > v_lastDistance)
|
2013-08-30 13:34:05 -07:00
|
|
|
distance = v_lastDistance;
|
2021-11-28 16:54:48 +01:00
|
|
|
|
2013-08-30 13:34:05 -07:00
|
|
|
VectorCopy(ent1->origin, newOrigin);
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
if (0 != ent1->player)
|
|
|
|
newOrigin[2] += 17; // head level of living player
|
2013-08-30 13:34:05 -07:00
|
|
|
else
|
2021-11-28 16:54:48 +01:00
|
|
|
newOrigin[2] += 8; // object, tricky, must be above bomb in CS
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
V_GetChaseOrigin(angle, newOrigin, distance, origin);
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
VectorCopy(angle, v_lastAngles);
|
|
|
|
}
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
void V_GetChasePos(int target, float* cl_angles, float* origin, float* angles)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
cl_entity_t* ent = NULL;
|
|
|
|
|
|
|
|
if (0 != target)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
ent = gEngfuncs.GetEntityByIndex(target);
|
2022-11-18 14:37:19 +01:00
|
|
|
}
|
2021-11-28 16:54:48 +01:00
|
|
|
|
2013-08-30 13:34:05 -07:00
|
|
|
if (!ent)
|
|
|
|
{
|
|
|
|
// just copy a save in-map position
|
2021-11-28 16:54:48 +01:00
|
|
|
VectorCopy(vJumpAngles, angles);
|
|
|
|
VectorCopy(vJumpOrigin, origin);
|
2013-08-30 13:34:05 -07:00
|
|
|
return;
|
|
|
|
}
|
2021-11-28 16:54:48 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (0 != gHUD.m_Spectator.m_autoDirector->value)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
if (0 != g_iUser3)
|
|
|
|
V_GetDirectedChasePosition(ent, gEngfuncs.GetEntityByIndex(g_iUser3),
|
|
|
|
angles, origin);
|
2013-08-30 13:34:05 -07:00
|
|
|
else
|
2021-11-28 16:54:48 +01:00
|
|
|
V_GetDirectedChasePosition(ent, (cl_entity_t*)0xFFFFFFFF,
|
|
|
|
angles, origin);
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
if (cl_angles == NULL) // no mouse angles given, use entity angles ( locked mode )
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
VectorCopy(ent->angles, angles);
|
|
|
|
angles[0] *= -1;
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
else
|
2021-11-28 16:54:48 +01:00
|
|
|
VectorCopy(cl_angles, angles);
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
VectorCopy(ent->origin, origin);
|
|
|
|
|
2021-03-16 20:44:35 +01:00
|
|
|
VectorAdd(origin, VEC_VIEW, origin); // some offset
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
V_GetChaseOrigin(angles, origin, cl_chasedist->value, origin);
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
v_resetCamera = false;
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
void V_ResetChaseCam()
|
|
|
|
{
|
|
|
|
v_resetCamera = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
void V_GetInEyePos(int target, float* origin, float* angles)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
if (0 == target)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
// just copy a save in-map position
|
2021-11-28 16:54:48 +01:00
|
|
|
VectorCopy(vJumpAngles, angles);
|
|
|
|
VectorCopy(vJumpOrigin, origin);
|
2013-08-30 13:34:05 -07:00
|
|
|
return;
|
2022-11-18 14:37:19 +01:00
|
|
|
}
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
cl_entity_t* ent = gEngfuncs.GetEntityByIndex(target);
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
if (!ent)
|
2013-08-30 13:34:05 -07:00
|
|
|
return;
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
VectorCopy(ent->origin, origin);
|
|
|
|
VectorCopy(ent->angles, angles);
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
angles[PITCH] *= -3.0f; // see CL_ProcessEntityUpdate()
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
if (ent->curstate.solid == SOLID_NOT)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
angles[ROLL] = 80; // dead view angle
|
2021-03-16 20:44:35 +01:00
|
|
|
VectorAdd(origin, VEC_DEAD_VIEW, origin);
|
|
|
|
}
|
|
|
|
else if (ent->curstate.usehull == 1)
|
|
|
|
{
|
|
|
|
VectorAdd(origin, VEC_DUCK_VIEW, origin);
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
else
|
|
|
|
// exacty eye position can't be caluculated since it depends on
|
|
|
|
// client values like cl_bobcycle, this offset matches the default values
|
2021-03-16 20:44:35 +01:00
|
|
|
VectorAdd(origin, VEC_VIEW, origin);
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
void V_GetMapFreePosition(float* cl_angles, float* origin, float* angles)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-03-16 19:45:46 +01:00
|
|
|
Vector forward;
|
|
|
|
Vector zScaledTarget;
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
VectorCopy(cl_angles, angles);
|
|
|
|
|
|
|
|
// modify angles since we don't wanna see map's bottom
|
2021-11-28 16:54:48 +01:00
|
|
|
angles[0] = 51.25f + 38.75f * (angles[0] / 90.0f);
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
zScaledTarget[0] = gHUD.m_Spectator.m_mapOrigin[0];
|
|
|
|
zScaledTarget[1] = gHUD.m_Spectator.m_mapOrigin[1];
|
2021-11-28 16:54:48 +01:00
|
|
|
zScaledTarget[2] = gHUD.m_Spectator.m_mapOrigin[2] * ((90.0f - angles[0]) / 90.0f);
|
|
|
|
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
AngleVectors(angles, forward, NULL, NULL);
|
|
|
|
|
|
|
|
VectorNormalize(forward);
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
VectorMA(zScaledTarget, -(4096.0f / gHUD.m_Spectator.m_mapZoom), forward, origin);
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
void V_GetMapChasePosition(int target, float* cl_angles, float* origin, float* angles)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-03-16 19:45:46 +01:00
|
|
|
Vector forward;
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
if (0 != target)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
cl_entity_t* ent = gEngfuncs.GetEntityByIndex(target);
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
if (0 != gHUD.m_Spectator.m_autoDirector->value)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
// this is done to get the angles made by director mode
|
|
|
|
V_GetChasePos(target, cl_angles, origin, angles);
|
|
|
|
VectorCopy(ent->origin, origin);
|
2021-11-28 16:54:48 +01:00
|
|
|
|
2013-08-30 13:34:05 -07:00
|
|
|
// keep fix chase angle horizontal
|
|
|
|
angles[0] = 45.0f;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
VectorCopy(cl_angles, angles);
|
|
|
|
VectorCopy(ent->origin, origin);
|
|
|
|
|
|
|
|
// modify angles since we don't wanna see map's bottom
|
2021-11-28 16:54:48 +01:00
|
|
|
angles[0] = 51.25f + 38.75f * (angles[0] / 90.0f);
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// keep out roaming position, but modify angles
|
|
|
|
VectorCopy(cl_angles, angles);
|
2021-11-28 16:54:48 +01:00
|
|
|
angles[0] = 51.25f + 38.75f * (angles[0] / 90.0f);
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
origin[2] *= ((90.0f - angles[0]) / 90.0f);
|
|
|
|
angles[2] = 0.0f; // don't roll angle (if chased player is dead)
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
AngleVectors(angles, forward, NULL, NULL);
|
|
|
|
|
|
|
|
VectorNormalize(forward);
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
VectorMA(origin, -1536, forward, origin);
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
int V_FindViewModelByWeaponModel(int weaponindex)
|
|
|
|
{
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
static const char* modelmap[][2] = {
|
|
|
|
|
|
|
|
{"models/p_crossbow.mdl", "models/v_crossbow.mdl"},
|
|
|
|
{"models/p_crowbar.mdl", "models/v_crowbar.mdl"},
|
|
|
|
{"models/p_egon.mdl", "models/v_egon.mdl"},
|
|
|
|
{"models/p_gauss.mdl", "models/v_gauss.mdl"},
|
|
|
|
{"models/p_9mmhandgun.mdl", "models/v_9mmhandgun.mdl"},
|
|
|
|
{"models/p_grenade.mdl", "models/v_grenade.mdl"},
|
|
|
|
{"models/p_hgun.mdl", "models/v_hgun.mdl"},
|
|
|
|
{"models/p_9mmAR.mdl", "models/v_9mmAR.mdl"},
|
|
|
|
{"models/p_357.mdl", "models/v_357.mdl"},
|
|
|
|
{"models/p_rpg.mdl", "models/v_rpg.mdl"},
|
|
|
|
{"models/p_shotgun.mdl", "models/v_shotgun.mdl"},
|
|
|
|
{"models/p_squeak.mdl", "models/v_squeak.mdl"},
|
|
|
|
{"models/p_tripmine.mdl", "models/v_tripmine.mdl"},
|
|
|
|
{"models/p_satchel_radio.mdl", "models/v_satchel_radio.mdl"},
|
|
|
|
{"models/p_satchel.mdl", "models/v_satchel.mdl"},
|
|
|
|
{NULL, NULL}};
|
|
|
|
|
|
|
|
struct model_s* weaponModel = IEngineStudio.GetModelByIndex(weaponindex);
|
|
|
|
|
|
|
|
if (weaponModel)
|
|
|
|
{
|
|
|
|
int len = strlen(weaponModel->name);
|
2013-08-30 13:34:05 -07:00
|
|
|
int i = 0;
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
while (modelmap[i] != NULL)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
if (!strnicmp(weaponModel->name, modelmap[i][0], len))
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
return gEngfuncs.pEventAPI->EV_FindModelIndex(modelmap[i][1]);
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
==================
|
|
|
|
V_CalcSpectatorRefdef
|
|
|
|
|
|
|
|
==================
|
|
|
|
*/
|
2021-11-28 16:54:48 +01:00
|
|
|
void V_CalcSpectatorRefdef(struct ref_params_s* pparams)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
static Vector velocity(0.0f, 0.0f, 0.0f);
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
static int lastWeaponModelIndex = 0;
|
|
|
|
static int lastViewModelIndex = 0;
|
2021-11-28 16:54:48 +01:00
|
|
|
|
|
|
|
cl_entity_t* ent = gEngfuncs.GetEntityByIndex(g_iUser2);
|
|
|
|
|
2021-11-28 15:32:26 +01:00
|
|
|
pparams->onlyClientDraw = 0;
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
// refresh position
|
2021-11-28 16:54:48 +01:00
|
|
|
VectorCopy(pparams->simorg, v_sim_org);
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
// get old values
|
2021-11-28 16:54:48 +01:00
|
|
|
VectorCopy(pparams->cl_viewangles, v_cl_angles);
|
|
|
|
VectorCopy(pparams->viewangles, v_angles);
|
|
|
|
VectorCopy(pparams->vieworg, v_origin);
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
if ((g_iUser1 == OBS_IN_EYE || gHUD.m_Spectator.m_pip->value == INSET_IN_EYE) && ent)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
// calculate player velocity
|
|
|
|
float timeDiff = ent->curstate.msg_time - ent->prevstate.msg_time;
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
if (timeDiff > 0)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-03-16 19:45:46 +01:00
|
|
|
Vector distance;
|
2013-08-30 13:34:05 -07:00
|
|
|
VectorSubtract(ent->prevstate.origin, ent->curstate.origin, distance);
|
2021-11-28 16:54:48 +01:00
|
|
|
VectorScale(distance, 1 / timeDiff, distance);
|
|
|
|
|
|
|
|
velocity[0] = velocity[0] * 0.9f + distance[0] * 0.1f;
|
|
|
|
velocity[1] = velocity[1] * 0.9f + distance[1] * 0.1f;
|
|
|
|
velocity[2] = velocity[2] * 0.9f + distance[2] * 0.1f;
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
VectorCopy(velocity, pparams->simvel);
|
|
|
|
}
|
|
|
|
|
|
|
|
// predict missing client data and set weapon model ( in HLTV mode or inset in eye mode )
|
2021-11-28 16:54:48 +01:00
|
|
|
if (0 != gEngfuncs.IsSpectateOnly())
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
V_GetInEyePos(g_iUser2, pparams->simorg, pparams->cl_viewangles);
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
pparams->health = 1;
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
cl_entity_t* gunModel = gEngfuncs.GetViewModel();
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
if (lastWeaponModelIndex != ent->curstate.weaponmodel)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
// weapon model changed
|
|
|
|
|
|
|
|
lastWeaponModelIndex = ent->curstate.weaponmodel;
|
2021-11-28 16:54:48 +01:00
|
|
|
lastViewModelIndex = V_FindViewModelByWeaponModel(lastWeaponModelIndex);
|
|
|
|
if (0 != lastViewModelIndex)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
gEngfuncs.pfnWeaponAnim(0, 0); // reset weapon animation
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// model not found
|
2021-11-28 16:54:48 +01:00
|
|
|
gunModel->model = NULL; // disable weapon model
|
2013-08-30 13:34:05 -07:00
|
|
|
lastWeaponModelIndex = lastViewModelIndex = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
if (0 != lastViewModelIndex)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
gunModel->model = IEngineStudio.GetModelByIndex(lastViewModelIndex);
|
2013-08-30 13:34:05 -07:00
|
|
|
gunModel->curstate.modelindex = lastViewModelIndex;
|
|
|
|
gunModel->curstate.frame = 0;
|
2021-11-28 16:54:48 +01:00
|
|
|
gunModel->curstate.colormap = 0;
|
2013-08-30 13:34:05 -07:00
|
|
|
gunModel->index = g_iUser2;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
gunModel->model = NULL; // disable weaopn model
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// only get viewangles from entity
|
2021-11-28 16:54:48 +01:00
|
|
|
VectorCopy(ent->angles, pparams->cl_viewangles);
|
|
|
|
pparams->cl_viewangles[PITCH] *= -3.0f; // see CL_ProcessEntityUpdate()
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
v_frametime = pparams->frametime;
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
if (pparams->nextView == 0)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
// first renderer cycle, full screen
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
switch (g_iUser1)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
case OBS_CHASE_LOCKED:
|
|
|
|
V_GetChasePos(g_iUser2, NULL, v_origin, v_angles);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OBS_CHASE_FREE:
|
|
|
|
V_GetChasePos(g_iUser2, v_cl_angles, v_origin, v_angles);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OBS_ROAMING:
|
|
|
|
VectorCopy(v_cl_angles, v_angles);
|
|
|
|
VectorCopy(v_sim_org, v_origin);
|
|
|
|
|
|
|
|
// override values if director is active
|
|
|
|
gHUD.m_Spectator.GetDirectorCamera(v_origin, v_angles);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OBS_IN_EYE:
|
|
|
|
V_CalcNormalRefdef(pparams);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OBS_MAP_FREE:
|
|
|
|
pparams->onlyClientDraw = 1;
|
|
|
|
V_GetMapFreePosition(v_cl_angles, v_origin, v_angles);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OBS_MAP_CHASE:
|
|
|
|
pparams->onlyClientDraw = 1;
|
|
|
|
V_GetMapChasePosition(g_iUser2, v_cl_angles, v_origin, v_angles);
|
|
|
|
break;
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
if (0 != gHUD.m_Spectator.m_pip->value)
|
|
|
|
pparams->nextView = 1; // force a second renderer view
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
gHUD.m_Spectator.m_iDrawCycle = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// second renderer cycle, inset window
|
|
|
|
|
|
|
|
// set inset parameters
|
2024-10-04 18:46:55 +02:00
|
|
|
pparams->viewport[0] = XRES_HD(gHUD.m_Spectator.m_OverviewData.insetWindowX); // change viewport to inset window
|
|
|
|
pparams->viewport[1] = YRES_HD(gHUD.m_Spectator.m_OverviewData.insetWindowY);
|
|
|
|
pparams->viewport[2] = XRES_HD(gHUD.m_Spectator.m_OverviewData.insetWindowWidth);
|
|
|
|
pparams->viewport[3] = YRES_HD(gHUD.m_Spectator.m_OverviewData.insetWindowHeight);
|
2021-11-28 16:54:48 +01:00
|
|
|
pparams->nextView = 0; // on further view
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
// override some settings in certain modes
|
2021-11-28 16:54:48 +01:00
|
|
|
switch ((int)gHUD.m_Spectator.m_pip->value)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
case INSET_CHASE_FREE:
|
|
|
|
V_GetChasePos(g_iUser2, v_cl_angles, v_origin, v_angles);
|
|
|
|
break;
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
case INSET_IN_EYE:
|
|
|
|
V_CalcNormalRefdef(pparams);
|
|
|
|
break;
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
case INSET_MAP_FREE:
|
|
|
|
pparams->onlyClientDraw = 1;
|
|
|
|
V_GetMapFreePosition(v_cl_angles, v_origin, v_angles);
|
|
|
|
break;
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
case INSET_MAP_CHASE:
|
|
|
|
pparams->onlyClientDraw = 1;
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
if (g_iUser1 == OBS_ROAMING)
|
|
|
|
V_GetMapChasePosition(0, v_cl_angles, v_origin, v_angles);
|
|
|
|
else
|
|
|
|
V_GetMapChasePosition(g_iUser2, v_cl_angles, v_origin, v_angles);
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
break;
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
gHUD.m_Spectator.m_iDrawCycle = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// write back new values into pparams
|
2021-11-28 16:54:48 +01:00
|
|
|
VectorCopy(v_cl_angles, pparams->cl_viewangles);
|
|
|
|
VectorCopy(v_angles, pparams->viewangles)
|
|
|
|
VectorCopy(v_origin, pparams->vieworg);
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
void DLLEXPORT V_CalcRefdef(struct ref_params_s* pparams)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
// RecClCalcRefdef(pparams);
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
// intermission / finale rendering
|
2021-11-28 16:54:48 +01:00
|
|
|
if (0 != pparams->intermission)
|
|
|
|
{
|
|
|
|
V_CalcIntermissionRefdef(pparams);
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
2021-11-28 16:54:48 +01:00
|
|
|
else if (0 != pparams->spectator || 0 != g_iUser1) // g_iUser true if in spectator mode
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
V_CalcSpectatorRefdef(pparams);
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
2021-11-28 16:54:48 +01:00
|
|
|
else if (0 == pparams->paused)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
V_CalcNormalRefdef(pparams);
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
/*
|
2013-08-30 13:34:05 -07:00
|
|
|
// Example of how to overlay the whole screen with red at 50 % alpha
|
|
|
|
#define SF_TEST
|
|
|
|
#if defined SF_TEST
|
|
|
|
{
|
|
|
|
screenfade_t sf;
|
|
|
|
gEngfuncs.pfnGetScreenFade( &sf );
|
|
|
|
|
|
|
|
sf.fader = 255;
|
|
|
|
sf.fadeg = 0;
|
|
|
|
sf.fadeb = 0;
|
|
|
|
sf.fadealpha = 128;
|
|
|
|
sf.fadeFlags = FFADE_STAYOUT | FFADE_OUT;
|
|
|
|
|
|
|
|
gEngfuncs.pfnSetScreenFade( &sf );
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
*/
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
=============
|
|
|
|
V_DropPunchAngle
|
|
|
|
|
|
|
|
=============
|
|
|
|
*/
|
2021-11-28 16:54:48 +01:00
|
|
|
void V_DropPunchAngle(float frametime, float* ev_punchangle)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
float len;
|
|
|
|
|
|
|
|
len = VectorNormalize(ev_punchangle);
|
2013-08-30 13:34:05 -07:00
|
|
|
len -= (10.0 + len * 0.5) * frametime;
|
2024-08-28 09:56:30 +02:00
|
|
|
len = V_max(len, 0.0f);
|
2021-11-28 16:54:48 +01:00
|
|
|
VectorScale(ev_punchangle, len, ev_punchangle);
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
=============
|
|
|
|
V_PunchAxis
|
|
|
|
|
|
|
|
Client side punch effect
|
|
|
|
=============
|
|
|
|
*/
|
2021-11-28 16:54:48 +01:00
|
|
|
void V_PunchAxis(int axis, float punch)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
ev_punchangle[axis] = punch;
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
=============
|
|
|
|
V_Init
|
|
|
|
=============
|
|
|
|
*/
|
2021-11-28 16:54:48 +01:00
|
|
|
void V_Init()
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
gEngfuncs.pfnAddCommand("centerview", V_StartPitchDrift);
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
scr_ofsx = gEngfuncs.pfnRegisterVariable("scr_ofsx", "0", 0);
|
|
|
|
scr_ofsy = gEngfuncs.pfnRegisterVariable("scr_ofsy", "0", 0);
|
|
|
|
scr_ofsz = gEngfuncs.pfnRegisterVariable("scr_ofsz", "0", 0);
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
v_centermove = gEngfuncs.pfnRegisterVariable("v_centermove", "0.15", 0);
|
|
|
|
v_centerspeed = gEngfuncs.pfnRegisterVariable("v_centerspeed", "500", 0);
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
cl_bobcycle = gEngfuncs.pfnRegisterVariable("cl_bobcycle", "0.8", 0); // best default for my experimental gun wag (sjb)
|
2024-08-28 09:56:30 +02:00
|
|
|
cl_bob = gEngfuncs.pfnRegisterVariable("cl_bob", "0.01", FCVAR_ARCHIVE); // best default for my experimental gun wag (sjb)
|
2021-11-28 16:54:48 +01:00
|
|
|
cl_bobup = gEngfuncs.pfnRegisterVariable("cl_bobup", "0.5", 0);
|
|
|
|
cl_waterdist = gEngfuncs.pfnRegisterVariable("cl_waterdist", "4", 0);
|
|
|
|
cl_chasedist = gEngfuncs.pfnRegisterVariable("cl_chasedist", "112", 0);
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//#define TRACE_TEST
|
2021-11-28 16:54:48 +01:00
|
|
|
#if defined(TRACE_TEST)
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
extern float in_fov;
|
|
|
|
/*
|
|
|
|
====================
|
|
|
|
CalcFov
|
|
|
|
====================
|
|
|
|
*/
|
2021-11-28 16:54:48 +01:00
|
|
|
float CalcFov(float fov_x, float width, float height)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
float a;
|
|
|
|
float x;
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
if (fov_x < 1 || fov_x > 179)
|
2021-11-28 16:54:48 +01:00
|
|
|
fov_x = 90; // error, set to 90
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
x = width / tan(fov_x / 360 * M_PI);
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
a = atan(height / x);
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
a = a * 360 / M_PI;
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
return a;
|
|
|
|
}
|
|
|
|
|
|
|
|
int hitent = -1;
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
void V_Move(int mx, int my)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
float fov;
|
|
|
|
float fx, fy;
|
|
|
|
float dx, dy;
|
|
|
|
float c_x, c_y;
|
|
|
|
float dX, dY;
|
2021-03-16 19:45:46 +01:00
|
|
|
Vector forward, up, right;
|
|
|
|
Vector newangles;
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-03-16 19:45:46 +01:00
|
|
|
Vector farpoint;
|
2013-08-30 13:34:05 -07:00
|
|
|
pmtrace_t tr;
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
fov = CalcFov(in_fov, (float)ScreenWidth, (float)ScreenHeight);
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
c_x = (float)ScreenWidth / 2.0;
|
|
|
|
c_y = (float)ScreenHeight / 2.0;
|
|
|
|
|
|
|
|
dx = (float)mx - c_x;
|
|
|
|
dy = (float)my - c_y;
|
|
|
|
|
|
|
|
// Proportion we moved in each direction
|
|
|
|
fx = dx / c_x;
|
|
|
|
fy = dy / c_y;
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
dX = fx * in_fov / 2.0;
|
2013-08-30 13:34:05 -07:00
|
|
|
dY = fy * fov / 2.0;
|
|
|
|
|
|
|
|
newangles = v_angles;
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
newangles[YAW] -= dX;
|
|
|
|
newangles[PITCH] += dY;
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
// Now rotate v_forward around that point
|
2021-11-28 16:54:48 +01:00
|
|
|
AngleVectors(newangles, forward, right, up);
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
farpoint = v_origin + 8192 * forward;
|
|
|
|
|
|
|
|
// Trace
|
2021-11-28 16:54:48 +01:00
|
|
|
tr = *(gEngfuncs.PM_TraceLine((float*)&v_origin, (float*)&farpoint, PM_TRACELINE_PHYSENTSONLY, 2 /*point sized hull*/, -1));
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
if (tr.fraction != 1.0 && tr.ent != 0)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
hitent = PM_GetPhysEntInfo(tr.ent);
|
|
|
|
PM_ParticleLine((float*)&v_origin, (float*)&tr.endpos, 5, 1.0, 0.0);
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
hitent = -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|