cvar_t cl_movement_friction = {0, "cl_movement_friction", "4", "how fast you slow down (should match sv_friction)"};
cvar_t cl_movement_wallfriction = {0, "cl_movement_wallfriction", "1", "how fast you slow down while sliding along a wall (should match sv_wallfriction)"};
cvar_t cl_movement_waterfriction = {0, "cl_movement_waterfriction", "-1", "how fast you slow down (should match sv_waterfriction), if less than 0 the cl_movement_friction variable is used instead"};
-cvar_t cl_movement_edgefriction = {0, "cl_movement_edgefriction", "2", "how much to slow down when you may be about to fall off a ledge (should match edgefriction)"};
+cvar_t cl_movement_edgefriction = {0, "cl_movement_edgefriction", "1", "how much to slow down when you may be about to fall off a ledge (should match edgefriction)"};
cvar_t cl_movement_stepheight = {0, "cl_movement_stepheight", "18", "how tall a step you can step in one instant (should match sv_stepheight)"};
cvar_t cl_movement_accelerate = {0, "cl_movement_accelerate", "10", "how fast you accelerate (should match sv_accelerate)"};
cvar_t cl_movement_airaccelerate = {0, "cl_movement_airaccelerate", "-1", "how fast you accelerate while in the air (should match sv_airaccelerate), if less than 0 the cl_movement_accelerate variable is used instead"};
cvar_t in_pitch_max = {0, "in_pitch_max", "90", "how far upward you can aim (quake used 80"};
cvar_t m_filter = {CVAR_SAVE, "m_filter","0", "smoothes mouse movement, less responsive but smoother aiming"};
+cvar_t m_accelerate = {CVAR_SAVE, "m_accelerate","1", "mouse acceleration factor (try 2)"};
+cvar_t m_accelerate_minspeed = {CVAR_SAVE, "m_accelerate_minspeed","5000", "below this speed, no acceleration is done"};
+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_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)"};
cl.cmd.upmove *= cl_movespeedkey.value;
}
- in_mouse_x = 0;
- in_mouse_y = 0;
-
// allow mice or other external controllers to add to the move
IN_Move ();
- // ignore a mouse move if mouse was activated/deactivated this frame
- if (cl_ignoremousemoves)
+ // apply m_accelerate if it is on
+ if(m_accelerate.value > 1)
{
- cl_ignoremousemoves--;
- in_mouse_x = 0;
- in_mouse_y = 0;
+ static float averagespeed = 0;
+ float speed, f, mi, ma;
+
+ speed = sqrt(in_mouse_x * in_mouse_x + in_mouse_y * in_mouse_y) / cl.realframetime;
+ if(m_accelerate_filter.value > 0)
+ f = bound(0, cl.realframetime / m_accelerate_filter.value, 1);
+ else
+ f = 1;
+ averagespeed = speed * f + averagespeed * (1 - f);
+
+ mi = max(1, m_accelerate_minspeed.value);
+ ma = max(m_accelerate_minspeed.value + 1, m_accelerate_maxspeed.value);
+
+ if(averagespeed <= mi)
+ {
+ f = 1;
+ }
+ else if(averagespeed >= ma)
+ {
+ f = m_accelerate.value;
+ }
+ else
+ {
+ /*
+ f = log(averagespeed);
+ mi = log(mi);
+ ma = log(ma);
+ */
+ f = averagespeed;
+ mi = mi;
+ ma = ma;
+ f = (f - mi) / (ma - mi) * (m_accelerate.value - 1) + 1;
+ }
+
+ in_mouse_x *= f;
+ in_mouse_y *= f;
}
// apply m_filter if it is on
old_mouse_x = mx;
old_mouse_y = my;
+ // ignore a mouse move if mouse was activated/deactivated this frame
+ if (cl_ignoremousemoves)
+ {
+ cl_ignoremousemoves--;
+ in_mouse_x = old_mouse_x = 0;
+ in_mouse_y = old_mouse_y = 0;
+ }
+
// if not in menu, apply mouse move to viewangles/movement
- if (!cl.csqc_wantsmousemove && in_client_mouse)
+ if (!key_consoleactive && key_dest == key_game && !cl.csqc_wantsmousemove)
{
float modulatedsensitivity = sensitivity.value * cl.sensitivityscale;
if (cl_prydoncursor.integer)
CL_ClientMovement_Move(s);
}
+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(s->cmd.forwardmove == 0 || s->cmd.sidemove != 0)
+ return;
+
+ 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
+ VectorMAM(speed, s->velocity, k, wishdir, s->velocity);
+ VectorNormalize(s->velocity);
+ }
+
+ VectorScale(s->velocity, speed, 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 accel, vec_t accelqw, vec_t sidefric)
+{
+ vec_t vel_straight, vel_z;
+ vec3_t vel_perpend;
+ vec_t addspeed;
+
+ vel_straight = DotProduct(s->velocity, wishdir);
+ vel_z = s->velocity[2];
+ VectorMA(s->velocity, -vel_straight, wishdir, vel_perpend); vel_perpend[2] -= vel_z;
+
+ addspeed = wishspeed - vel_straight;
+ if(addspeed > 0)
+ vel_straight = vel_straight + min(addspeed, accel * s->cmd.frametime * wishspeed) * accelqw;
+ if(wishspeed > 0)
+ vel_straight = vel_straight + min(wishspeed, accel * s->cmd.frametime * wishspeed) * (1 - accelqw);
+
+ VectorScale(vel_perpend, 1 - s->cmd.frametime * wishspeed * sidefric, vel_perpend);
+
+ VectorMA(vel_perpend, vel_straight, wishdir, s->velocity);
+ s->velocity[2] += vel_z;
+}
+
+void CL_ClientMovement_Physics_PM_AirAccelerate(cl_clientmovement_state_t *s, vec3_t wishdir, vec_t wishspeed)
+{
+ vec3_t curvel, wishvel, acceldir, curdir;
+ float addspeed, accelspeed, curspeed;
+ float dot;
+
+ float airforwardaccel = cl.movevars_warsowbunny_airforwardaccel;
+ float bunnyaccel = cl.movevars_warsowbunny_accel;
+ float bunnytopspeed = cl.movevars_warsowbunny_topspeed;
+ float turnaccel = cl.movevars_warsowbunny_turnaccel;
+ float backtosideratio = cl.movevars_warsowbunny_backtosideratio;
+
+ if( !wishspeed )
+ return;
+
+ VectorCopy( s->velocity, curvel );
+ curvel[2] = 0;
+ curspeed = VectorLength( curvel );
+
+ if( wishspeed > curspeed * 1.01f )
+ {
+ float accelspeed = curspeed + airforwardaccel * cl.movevars_maxairspeed * s->cmd.frametime;
+ if( accelspeed < wishspeed )
+ wishspeed = accelspeed;
+ }
+ else
+ {
+ float f = ( bunnytopspeed - curspeed ) / ( bunnytopspeed - cl.movevars_maxairspeed );
+ if( f < 0 )
+ f = 0;
+ wishspeed = max( curspeed, cl.movevars_maxairspeed ) + bunnyaccel * f * cl.movevars_maxairspeed * s->cmd.frametime;
+ }
+ VectorScale( wishdir, wishspeed, wishvel );
+ VectorSubtract( wishvel, curvel, acceldir );
+ addspeed = VectorNormalizeLength( acceldir );
+
+ accelspeed = turnaccel * cl.movevars_maxairspeed /* wishspeed */ * s->cmd.frametime;
+ if( accelspeed > addspeed )
+ accelspeed = addspeed;
+
+ if( backtosideratio < 1.0f )
+ {
+ VectorNormalize2( curvel, curdir );
+ dot = DotProduct( acceldir, curdir );
+ if( dot < 0 )
+ VectorMA( acceldir, -( 1.0f - backtosideratio ) * dot, curdir, acceldir );
+ }
+
+ VectorMA( s->velocity, accelspeed, acceldir, s->velocity );
+}
+
void CL_ClientMovement_Physics_Walk(cl_clientmovement_state_t *s)
{
vec_t friction;
VectorScale(wishvel, 1 / wishspeed, wishdir);
else
VectorSet( wishdir, 0.0, 0.0, 0.0 );
- wishspeed = min(wishspeed, cl.movevars_maxspeed);
- if (s->crouched)
- wishspeed *= 0.5;
-
// check if onground
if (s->onground)
{
+ wishspeed = min(wishspeed, cl.movevars_maxspeed);
+ if (s->crouched)
+ wishspeed *= 0.5;
+
// apply edge friction
f = sqrt(s->velocity[0] * s->velocity[0] + s->velocity[1] * s->velocity[1]);
- friction = cl.movevars_friction;
- if (f > 0 && cl.movevars_edgefriction != 1)
+ if (f > 0)
{
- vec3_t neworigin2;
- vec3_t neworigin3;
- // note: QW uses the full player box for the trace, and yet still
- // uses s->origin[2] + s->mins[2], which is clearly an bug, but
- // this mimics it for compatibility
- VectorSet(neworigin2, s->origin[0] + s->velocity[0]*(16/f), s->origin[1] + s->velocity[1]*(16/f), s->origin[2] + s->mins[2]);
- VectorSet(neworigin3, neworigin2[0], neworigin2[1], neworigin2[2] - 34);
- if (cls.protocol == PROTOCOL_QUAKEWORLD)
- trace = CL_Move(neworigin2, s->mins, s->maxs, neworigin3, MOVE_NORMAL, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_PLAYERCLIP, true, true, NULL, false);
- else
- trace = CL_Move(neworigin2, vec3_origin, vec3_origin, neworigin3, MOVE_NORMAL, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_PLAYERCLIP, true, true, NULL, false);
- if (trace.fraction == 1 && !trace.startsolid)
- friction *= cl.movevars_edgefriction;
+ friction = cl.movevars_friction;
+ if (cl.movevars_edgefriction != 1)
+ {
+ vec3_t neworigin2;
+ vec3_t neworigin3;
+ // note: QW uses the full player box for the trace, and yet still
+ // uses s->origin[2] + s->mins[2], which is clearly an bug, but
+ // this mimics it for compatibility
+ VectorSet(neworigin2, s->origin[0] + s->velocity[0]*(16/f), s->origin[1] + s->velocity[1]*(16/f), s->origin[2] + s->mins[2]);
+ VectorSet(neworigin3, neworigin2[0], neworigin2[1], neworigin2[2] - 34);
+ if (cls.protocol == PROTOCOL_QUAKEWORLD)
+ trace = CL_Move(neworigin2, s->mins, s->maxs, neworigin3, MOVE_NORMAL, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_PLAYERCLIP, true, true, NULL, false);
+ else
+ trace = CL_Move(neworigin2, vec3_origin, vec3_origin, neworigin3, MOVE_NORMAL, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_PLAYERCLIP, true, true, NULL, false);
+ if (trace.fraction == 1 && !trace.startsolid)
+ friction *= cl.movevars_edgefriction;
+ }
+ // apply ground friction
+ f = 1 - s->cmd.frametime * friction * ((f < cl.movevars_stopspeed) ? (cl.movevars_stopspeed / f) : 1);
+ f = max(f, 0);
+ VectorScale(s->velocity, f, s->velocity);
}
- // apply ground friction
- f = 1 - s->cmd.frametime * friction * ((f < cl.movevars_stopspeed) ? (cl.movevars_stopspeed / f) : 1);
- f = max(f, 0);
- VectorScale(s->velocity, f, s->velocity);
addspeed = wishspeed - DotProduct(s->velocity, wishdir);
if (addspeed > 0)
{
{
if (s->waterjumptime <= 0)
{
- vec_t f;
- vec_t vel_straight;
- vec_t vel_z;
- vec3_t vel_perpend;
-
// apply air speed limit
- wishspeed = min(wishspeed, cl.movevars_maxairspeed);
+ vec_t accel, wishspeed2, accelqw;
+ qboolean accelerating;
- /*
- addspeed = wishspeed - DotProduct(s->velocity, wishdir);
- if (addspeed > 0)
+ accelqw = cl.movevars_airaccel_qw;
+ wishspeed = min(wishspeed, cl.movevars_maxairspeed);
+ if (s->crouched)
+ wishspeed *= 0.5;
+ accel = cl.movevars_airaccelerate;
+
+ accelerating = (DotProduct(s->velocity, wishdir) > 0);
+ wishspeed2 = wishspeed;
+
+ // 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)
{
- accelspeed = min(cl.movevars_accelerate * s->q.frametime * wishspeed, addspeed);
- VectorMA(s->velocity, accelspeed, wishdir, s->velocity);
+ 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)
+ }
}
- */
-
- vel_straight = DotProduct(s->velocity, wishdir);
- vel_z = s->velocity[2];
- VectorMA(s->velocity, -vel_straight, wishdir, vel_perpend);
- vel_perpend[2] -= vel_z;
+ // !CPM
- f = wishspeed - vel_straight;
- if(f > 0)
- vel_straight += min(f, cl.movevars_airaccelerate * s->cmd.frametime * wishspeed) * cl.movevars_airaccel_qw;
- if(wishspeed > 0)
- vel_straight += min(wishspeed, cl.movevars_airaccelerate * s->cmd.frametime * wishspeed) * (1 - cl.movevars_airaccel_qw);
-
- VectorM(1 - (s->cmd.frametime * (wishspeed / cl.movevars_maxairspeed) * cl.movevars_airaccel_sideways_friction), vel_perpend, vel_perpend);
+ 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, accel, accelqw, cl.movevars_airaccel_sideways_friction / cl.movevars_maxairspeed);
- VectorMA(vel_perpend, vel_straight, wishdir, s->velocity);
- s->velocity[2] += vel_z;
+ 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;
CL_ClientMovement_Move(s);
cl.movevars_friction = cl.statsf[STAT_MOVEVARS_FRICTION];
cl.movevars_wallfriction = cl.statsf[STAT_MOVEVARS_WALLFRICTION];
cl.movevars_waterfriction = cl.statsf[STAT_MOVEVARS_WATERFRICTION];
+ 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_aircontrol = cl.statsf[STAT_MOVEVARS_AIRCONTROL];
+ 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];
}
else
{
cl.movevars_stepheight = cl_movement_stepheight.value;
cl.movevars_airaccel_qw = cl_movement_airaccel_qw.value;
cl.movevars_airaccel_sideways_friction = cl_movement_airaccel_sideways_friction.value;
+ cl.movevars_airstopaccelerate = 0;
+ cl.movevars_airstrafeaccelerate = 0;
+ cl.movevars_maxairstrafespeed = 0;
+ cl.movevars_aircontrol = 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;
}
}
if (bits & QW_CM_ANGLE3)
MSG_WriteAngle16i(buf, to->viewangles[2]);
if (bits & QW_CM_FORWARD)
- MSG_WriteShort(buf, to->forwardmove);
+ MSG_WriteShort(buf, (short) to->forwardmove);
if (bits & QW_CM_SIDE)
- MSG_WriteShort(buf, to->sidemove);
+ MSG_WriteShort(buf, (short) to->sidemove);
if (bits & QW_CM_UP)
- MSG_WriteShort(buf, to->upmove);
+ MSG_WriteShort(buf, (short) to->upmove);
if (bits & QW_CM_BUTTONS)
MSG_WriteByte(buf, to->buttons);
if (bits & QW_CM_IMPULSE)
packettime = 0;
// always send if buttons changed or an impulse is pending
// even if it violates the rate limit!
- if (!cl_netimmediatebuttons.integer || (cl.movecmd[0].buttons == cl.movecmd[1].buttons && !cl.movecmd[0].impulse))
+ if (!cl.movecmd[0].impulse && (!cl_netimmediatebuttons.integer || cl.movecmd[0].buttons == cl.movecmd[1].buttons))
{
// don't choke the connection with packets (obey rate limit)
if ((cls.protocol == PROTOCOL_QUAKEWORLD || cls.signon == SIGNONS) && !NetConn_CanSend(cls.netcon) && !cl.islocalgame)
Cvar_RegisterVariable(&in_pitch_min);
Cvar_RegisterVariable(&in_pitch_max);
Cvar_RegisterVariable(&m_filter);
+ Cvar_RegisterVariable(&m_accelerate);
+ Cvar_RegisterVariable(&m_accelerate_minspeed);
+ Cvar_RegisterVariable(&m_accelerate_maxspeed);
+ Cvar_RegisterVariable(&m_accelerate_filter);
Cvar_RegisterVariable(&cl_netfps);
Cvar_RegisterVariable(&cl_netrepeatinput);