cvar_t m_accelerate_maxspeed = {CVAR_SAVE, "m_accelerate_maxspeed","10000", "above this speed, full acceleration is done"};
cvar_t m_accelerate_filter = {CVAR_SAVE, "m_accelerate_filter","0.1", "mouse acceleration factor filtering"};
-cvar_t cl_netfps = {CVAR_SAVE, "cl_netfps","20", "how many input packets to send to server each second"};
+cvar_t cl_netfps = {CVAR_SAVE, "cl_netfps","72", "how many input packets to send to server each second"};
cvar_t cl_netrepeatinput = {CVAR_SAVE, "cl_netrepeatinput", "1", "how many packets in a row can be lost without movement issues when using cl_movement (technically how many input messages to repeat in each packet that have not yet been acknowledged by the server), only affects DP7 and later servers (Quake uses 0, QuakeWorld uses 2, and just for comparison Quake3 uses 1)"};
cvar_t cl_netimmediatebuttons = {CVAR_SAVE, "cl_netimmediatebuttons", "1", "sends extra packets whenever your buttons change or an impulse is used (basically: whenever you click fire or change weapon)"};
}
// if not in menu, apply mouse move to viewangles/movement
- if (!key_consoleactive && key_dest == key_game && !cl.csqc_wantsmousemove)
+ if (!key_consoleactive && key_dest == key_game && !cl.csqc_wantsmousemove && cl_prydoncursor.integer <= 0)
{
float modulatedsensitivity = sensitivity.value * cl.sensitivityscale;
- if (cl_prydoncursor.integer)
- {
- // mouse interacting with the scene, mostly stationary view
- V_StopPitchDrift();
- cl.cmd.cursor_screen[0] += in_mouse_x * modulatedsensitivity / vid.width;
- cl.cmd.cursor_screen[1] += in_mouse_y * modulatedsensitivity / vid.height;
- }
- else if (in_strafe.state & 1)
+ if (in_strafe.state & 1)
{
// strafing mode, all looking is movement
V_StopPitchDrift();
}
}
else // don't pitch drift when csqc is controlling the mouse
+ {
+ // mouse interacting with the scene, mostly stationary view
V_StopPitchDrift();
+ // update prydon cursor
+ cl.cmd.cursor_screen[0] = in_windowmouse_x * 2.0 / vid.width - 1.0;
+ cl.cmd.cursor_screen[1] = in_windowmouse_y * 2.0 / vid.height - 1.0;
+ }
if(v_flipped.integer)
{
{
vec3_t temp;
- if (!cl_prydoncursor.integer)
+ if (cl_prydoncursor.integer <= 0)
VectorClear(cl.cmd.cursor_screen);
/*
VectorSet(temp, cl.cmd.cursor_screen[2] * 1000000, (v_flipped.integer ? -1 : 1) * cl.cmd.cursor_screen[0] * -r_refdef.view.frustum_x * 1000000, cl.cmd.cursor_screen[1] * -r_refdef.view.frustum_y * 1000000);
Matrix4x4_Transform(&r_refdef.view.matrix, temp, cl.cmd.cursor_end);
// trace from view origin to the cursor
- cl.cmd.cursor_fraction = CL_SelectTraceLine(cl.cmd.cursor_start, cl.cmd.cursor_end, cl.cmd.cursor_impact, cl.cmd.cursor_normal, &cl.cmd.cursor_entitynumber, (chase_active.integer || cl.intermission) ? &cl.entities[cl.playerentity].render : NULL);
+ if (cl_prydoncursor_notrace.integer)
+ {
+ cl.cmd.cursor_fraction = 1.0f;
+ VectorCopy(cl.cmd.cursor_end, cl.cmd.cursor_impact);
+ VectorClear(cl.cmd.cursor_normal);
+ cl.cmd.cursor_entitynumber = 0;
+ }
+ else
+ cl.cmd.cursor_fraction = CL_SelectTraceLine(cl.cmd.cursor_start, cl.cmd.cursor_end, cl.cmd.cursor_impact, cl.cmd.cursor_normal, &cl.cmd.cursor_entitynumber, (chase_active.integer || cl.intermission) ? &cl.entities[cl.playerentity].render : NULL);
}
typedef enum waterlevel_e
CL_ClientMovement_Move(s);
}
+static vec_t CL_IsMoveInDirection(vec_t forward, vec_t side, vec_t angle)
+{
+ if(forward == 0 && side == 0)
+ return 0; // avoid division by zero
+ angle -= RAD2DEG(atan2(side, forward));
+ angle = (ANGLEMOD(angle + 180) - 180) / 45;
+ if(angle > 1)
+ return 0;
+ if(angle < -1)
+ return 0;
+ return 1 - fabs(angle);
+}
+
+static vec_t CL_GeomLerp(vec_t a, vec_t lerp, vec_t b)
+{
+ if(a == 0)
+ {
+ if(lerp < 1)
+ return 0;
+ else
+ return b;
+ }
+ if(b == 0)
+ {
+ if(lerp > 0)
+ return 0;
+ else
+ return a;
+ }
+ return a * pow(fabs(b / a), lerp);
+}
+
void CL_ClientMovement_Physics_CPM_PM_Aircontrol(cl_clientmovement_state_t *s, vec3_t wishdir, vec_t wishspeed)
{
vec_t zspeed, speed, dot, k;
+#if 0
+ // this doesn't play well with analog input
if(s->cmd.forwardmove == 0 || s->cmd.sidemove != 0)
return;
-
+ k = 32;
+#else
+ k = 32 * (2 * CL_IsMoveInDirection(s->cmd.forwardmove, s->cmd.sidemove, 0) - 1);
+ if(k <= 0)
+ return;
+#endif
+
+ k *= bound(0, wishspeed / cl.movevars_maxairspeed, 1);
+
zspeed = s->velocity[2];
s->velocity[2] = 0;
speed = VectorNormalizeLength(s->velocity);
dot = DotProduct(s->velocity, wishdir);
- k = 32;
- k *= cl.movevars_aircontrol*dot*dot*s->cmd.frametime;
if(dot > 0) { // we can't change direction while slowing down
+ k *= pow(dot, cl.movevars_aircontrol_power)*s->cmd.frametime;
+ speed = max(0, speed - cl.movevars_aircontrol_penalty * sqrt(max(0, 1 - dot*dot)) * k/32);
+ k *= cl.movevars_aircontrol;
VectorMAM(speed, s->velocity, k, wishdir, s->velocity);
VectorNormalize(s->velocity);
}
s->velocity[2] = zspeed;
}
-void CL_ClientMovement_Physics_PM_Accelerate(cl_clientmovement_state_t *s, vec3_t wishdir, vec_t wishspeed, vec_t wishspeed0, vec_t accel, vec_t accelqw, vec_t sidefric)
+float CL_ClientMovement_Physics_AdjustAirAccelQW(float accelqw, float factor)
+{
+ return
+ (accelqw < 0 ? -1 : +1)
+ *
+ bound(0.000001, 1 - (1 - fabs(accelqw)) * factor, 1);
+}
+
+void CL_ClientMovement_Physics_PM_Accelerate(cl_clientmovement_state_t *s, vec3_t wishdir, vec_t wishspeed, vec_t wishspeed0, vec_t accel, vec_t accelqw, vec_t sidefric, vec_t speedlimit)
{
vec_t vel_straight;
vec_t vel_z;
step = accel * s->cmd.frametime * wishspeed0;
vel_xy_current = VectorLength(vel_xy);
+ if(speedlimit > 0)
+ accelqw = CL_ClientMovement_Physics_AdjustAirAccelQW(accelqw, (speedlimit - bound(wishspeed, vel_xy_current, speedlimit)) / max(1, speedlimit - wishspeed));
vel_xy_forward = vel_xy_current + bound(0, wishspeed - vel_xy_current, step) * accelqw + step * (1 - accelqw);
vel_xy_backward = vel_xy_current - bound(0, wishspeed + vel_xy_current, step) * accelqw - step * (1 - accelqw);
if(vel_xy_backward < 0)
// negative: only apply so much sideways friction to stay below the speed you could get by "braking"
{
vec_t f, fmin;
- f = 1 - s->cmd.frametime * wishspeed * sidefric;
+ f = max(0, 1 + s->cmd.frametime * wishspeed * sidefric);
fmin = (vel_xy_backward*vel_xy_backward - vel_straight*vel_straight) / VectorLength2(vel_perpend);
+ // assume: fmin > 1
+ // vel_xy_backward*vel_xy_backward - vel_straight*vel_straight > vel_perpend*vel_perpend
+ // vel_xy_backward*vel_xy_backward > vel_straight*vel_straight + vel_perpend*vel_perpend
+ // vel_xy_backward*vel_xy_backward > vel_xy * vel_xy
+ // obviously, this cannot be
if(fmin <= 0)
VectorScale(vel_perpend, f, vel_perpend);
else
- VectorScale(vel_perpend, min(1.0f, max(fmin, f)), vel_perpend);
+ {
+ fmin = sqrt(fmin);
+ VectorScale(vel_perpend, max(fmin, f), vel_perpend);
+ }
}
else
- VectorScale(vel_perpend, 1 - s->cmd.frametime * wishspeed * sidefric, vel_perpend);
+ VectorScale(vel_perpend, max(0, 1 - s->cmd.frametime * wishspeed * sidefric), vel_perpend);
VectorMA(vel_perpend, vel_straight, wishdir, s->velocity);
vec_t addspeed;
vec_t accelspeed;
vec_t f;
+ vec_t gravity;
vec3_t forward;
vec3_t right;
vec3_t up;
accelspeed = min(cl.movevars_accelerate * s->cmd.frametime * wishspeed, addspeed);
VectorMA(s->velocity, accelspeed, wishdir, s->velocity);
}
- s->velocity[2] -= cl.movevars_gravity * cl.movevars_entgravity * s->cmd.frametime;
+ if(cl.moveflags & MOVEFLAG_NOGRAVITYONGROUND)
+ gravity = 0;
+ else
+ gravity = cl.movevars_gravity * cl.movevars_entgravity * s->cmd.frametime;
+ if(cl.moveflags & MOVEFLAG_GRAVITYUNAFFECTEDBYTICRATE)
+ s->velocity[2] -= gravity * 0.5f;
+ else
+ s->velocity[2] -= gravity;
if (cls.protocol == PROTOCOL_QUAKEWORLD)
s->velocity[2] = 0;
if (VectorLength2(s->velocity))
CL_ClientMovement_Move(s);
+ if(cl.moveflags & MOVEFLAG_GRAVITYUNAFFECTEDBYTICRATE)
+ s->velocity[2] -= gravity * 0.5f;
}
else
{
if (s->waterjumptime <= 0)
{
// apply air speed limit
- vec_t accel, wishspeed0, wishspeed2, accelqw;
+ vec_t accel, wishspeed0, wishspeed2, accelqw, strafity;
qboolean accelerating;
accelqw = cl.movevars_airaccel_qw;
// CPM: air control
if(cl.movevars_airstopaccelerate != 0)
- if(DotProduct(s->velocity, wishdir) < 0)
- accel = cl.movevars_airstopaccelerate;
- if(s->cmd.forwardmove == 0 && s->cmd.sidemove != 0)
{
- if(cl.movevars_maxairstrafespeed)
- {
- if(wishspeed > cl.movevars_maxairstrafespeed)
- wishspeed = cl.movevars_maxairstrafespeed;
- if(cl.movevars_maxairstrafespeed < cl.movevars_maxairspeed)
- accelqw = 1;
- // otherwise, CPMA-style air acceleration misbehaves a lot
- // if partially non-QW acceleration is used (as in, strafing
- // would get faster than moving forward straight)
- }
- if(cl.movevars_airstrafeaccelerate)
- {
- accel = cl.movevars_airstrafeaccelerate;
- if(cl.movevars_airstrafeaccelerate > cl.movevars_airaccelerate)
- accelqw = 1;
- // otherwise, CPMA-style air acceleration misbehaves a lot
- // if partially non-QW acceleration is used (as in, strafing
- // would get faster than moving forward straight)
- }
+ vec3_t curdir;
+ curdir[0] = s->velocity[0];
+ curdir[1] = s->velocity[1];
+ curdir[2] = 0;
+ VectorNormalize(curdir);
+ accel = accel + (cl.movevars_airstopaccelerate - accel) * max(0, -DotProduct(curdir, wishdir));
}
+ strafity = CL_IsMoveInDirection(s->cmd.forwardmove, s->cmd.sidemove, -90) + CL_IsMoveInDirection(s->cmd.forwardmove, s->cmd.sidemove, +90); // if one is nonzero, other is always zero
+ if(cl.movevars_maxairstrafespeed)
+ wishspeed = min(wishspeed, CL_GeomLerp(cl.movevars_maxairspeed, strafity, cl.movevars_maxairstrafespeed));
+ if(cl.movevars_airstrafeaccelerate)
+ accel = CL_GeomLerp(cl.movevars_airaccelerate, strafity, cl.movevars_airstrafeaccelerate);
+ if(cl.movevars_airstrafeaccel_qw)
+ accelqw =
+ (((strafity > 0.5 ? cl.movevars_airstrafeaccel_qw : cl.movevars_airaccel_qw) >= 0) ? +1 : -1)
+ *
+ (1 - CL_GeomLerp(1 - fabs(cl.movevars_airaccel_qw), strafity, 1 - fabs(cl.movevars_airstrafeaccel_qw)));
// !CPM
if(cl.movevars_warsowbunny_turnaccel && accelerating && s->cmd.sidemove == 0 && s->cmd.forwardmove != 0)
CL_ClientMovement_Physics_PM_AirAccelerate(s, wishdir, wishspeed2);
else
- CL_ClientMovement_Physics_PM_Accelerate(s, wishdir, wishspeed, wishspeed0, accel, accelqw, cl.movevars_airaccel_sideways_friction / cl.movevars_maxairspeed);
+ CL_ClientMovement_Physics_PM_Accelerate(s, wishdir, wishspeed, wishspeed0, accel, accelqw, cl.movevars_airaccel_sideways_friction / cl.movevars_maxairspeed, cl.movevars_airspeedlimit_nonqw);
if(cl.movevars_aircontrol)
CL_ClientMovement_Physics_CPM_PM_Aircontrol(s, wishdir, wishspeed2);
}
- s->velocity[2] -= cl.movevars_gravity * cl.movevars_entgravity * s->cmd.frametime;
+ gravity = cl.movevars_gravity * cl.movevars_entgravity * s->cmd.frametime;
+ if(cl.moveflags & MOVEFLAG_GRAVITYUNAFFECTEDBYTICRATE)
+ s->velocity[2] -= gravity * 0.5f;
+ else
+ s->velocity[2] -= gravity;
CL_ClientMovement_Move(s);
+ if(cl.moveflags & MOVEFLAG_GRAVITYUNAFFECTEDBYTICRATE)
+ s->velocity[2] -= gravity * 0.5f;
}
}
cl.movevars_airstopaccelerate = cl.statsf[STAT_MOVEVARS_AIRSTOPACCELERATE];
cl.movevars_airstrafeaccelerate = cl.statsf[STAT_MOVEVARS_AIRSTRAFEACCELERATE];
cl.movevars_maxairstrafespeed = cl.statsf[STAT_MOVEVARS_MAXAIRSTRAFESPEED];
+ cl.movevars_airstrafeaccel_qw = cl.statsf[STAT_MOVEVARS_AIRSTRAFEACCEL_QW];
cl.movevars_aircontrol = cl.statsf[STAT_MOVEVARS_AIRCONTROL];
+ cl.movevars_aircontrol_power = cl.statsf[STAT_MOVEVARS_AIRCONTROL_POWER];
+ cl.movevars_aircontrol_penalty = cl.statsf[STAT_MOVEVARS_AIRCONTROL_PENALTY];
cl.movevars_warsowbunny_airforwardaccel = cl.statsf[STAT_MOVEVARS_WARSOWBUNNY_AIRFORWARDACCEL];
cl.movevars_warsowbunny_accel = cl.statsf[STAT_MOVEVARS_WARSOWBUNNY_ACCEL];
cl.movevars_warsowbunny_topspeed = cl.statsf[STAT_MOVEVARS_WARSOWBUNNY_TOPSPEED];
cl.movevars_warsowbunny_turnaccel = cl.statsf[STAT_MOVEVARS_WARSOWBUNNY_TURNACCEL];
cl.movevars_warsowbunny_backtosideratio = cl.statsf[STAT_MOVEVARS_WARSOWBUNNY_BACKTOSIDERATIO];
+ cl.movevars_airspeedlimit_nonqw = cl.statsf[STAT_MOVEVARS_AIRSPEEDLIMIT_NONQW];
}
else
{
cl.movevars_airstopaccelerate = 0;
cl.movevars_airstrafeaccelerate = 0;
cl.movevars_maxairstrafespeed = 0;
+ cl.movevars_airstrafeaccel_qw = 0;
cl.movevars_aircontrol = 0;
+ cl.movevars_aircontrol_power = 2;
+ cl.movevars_aircontrol_penalty = 0;
cl.movevars_warsowbunny_airforwardaccel = 0;
cl.movevars_warsowbunny_accel = 0;
cl.movevars_warsowbunny_topspeed = 0;
cl.movevars_warsowbunny_turnaccel = 0;
cl.movevars_warsowbunny_backtosideratio = 0;
+ cl.movevars_airspeedlimit_nonqw = 0;
}
if(!(cl.moveflags & MOVEFLAG_VALID))
if(gamemode == GAME_NEXUIZ)
cl.moveflags = MOVEFLAG_Q2AIRACCELERATE;
}
+
+ if(cl.movevars_aircontrol_power <= 0)
+ cl.movevars_aircontrol_power = 2; // CPMA default
}
void CL_ClientMovement_Replay(void)
if (in_button8.state & 3) bits |= 128;
if (in_use.state & 3) bits |= 256;
if (key_dest != key_game || key_consoleactive) bits |= 512;
- if (cl_prydoncursor.integer) bits |= 1024;
+ if (cl_prydoncursor.integer > 0) bits |= 1024;
if (in_button9.state & 3) bits |= 2048;
if (in_button10.state & 3) bits |= 4096;
if (in_button11.state & 3) bits |= 8192;
if (in_button16.state & 3) bits |= 262144;
// button bits 19-31 unused currently
// rotate/zoom view serverside if PRYDON_CLIENTCURSOR cursor is at edge of screen
- if (cl.cmd.cursor_screen[0] <= -1) bits |= 8;
- if (cl.cmd.cursor_screen[0] >= 1) bits |= 16;
- if (cl.cmd.cursor_screen[1] <= -1) bits |= 32;
- if (cl.cmd.cursor_screen[1] >= 1) bits |= 64;
+ if(cl_prydoncursor.integer > 0)
+ {
+ if (cl.cmd.cursor_screen[0] <= -1) bits |= 8;
+ if (cl.cmd.cursor_screen[0] >= 1) bits |= 16;
+ if (cl.cmd.cursor_screen[1] <= -1) bits |= 32;
+ if (cl.cmd.cursor_screen[1] >= 1) bits |= 64;
+ }
// set buttons and impulse
cl.cmd.buttons = bits;