+ */
+
+ vel_straight = DotProduct(s->velocity, wishdir);
+ vel_z = s->velocity[2];
+ VectorMA(s->velocity, -vel_straight, wishdir, vel_perpend);
+ vel_perpend[2] -= vel_z;
+
+ f = wishspeed - vel_straight;
+ if(f > 0)
+ vel_straight += min(f, cl.movevars_airaccelerate * s->q.frametime * wishspeed) * cl.movevars_airaccel_qw;
+ if(wishspeed > 0)
+ vel_straight += min(wishspeed, cl.movevars_airaccelerate * s->q.frametime * wishspeed) * (1 - cl.movevars_airaccel_qw);
+
+ VectorM(1 - (s->q.frametime * (wishspeed / cl.movevars_maxairspeed) * cl.movevars_airaccel_sideways_friction), vel_perpend, vel_perpend);
+
+ VectorMA(vel_perpend, vel_straight, wishdir, s->velocity);
+ s->velocity[2] += vel_z;
+ }
+ s->velocity[2] -= cl.movevars_gravity * cl.movevars_entgravity * s->q.frametime;
+ CL_ClientMovement_Move(s);
+ }
+}
+
+void CL_ClientMovement_PlayerMove(cl_clientmovement_state_t *s)
+{
+ //Con_Printf(" %f", frametime);
+ if (!s->q.jump)
+ s->q.canjump = true;
+ s->waterjumptime -= s->q.frametime;
+ CL_ClientMovement_UpdateStatus(s);
+ if (s->waterlevel >= WATERLEVEL_SWIMMING)
+ CL_ClientMovement_Physics_Swim(s);
+ else
+ CL_ClientMovement_Physics_Walk(s);
+}
+
+extern cvar_t slowmo;
+void CL_UpdateMoveVars(void)
+{
+ if (cls.protocol == PROTOCOL_QUAKEWORLD)
+ cl.movevars_packetinterval = 1.0 / bound(1, cl_netinputpacketspersecond_qw.value, 100);
+ else if (cl.stats[STAT_MOVEVARS_TICRATE])
+ {
+ cl.movevars_packetinterval = cl.statsf[STAT_MOVEVARS_TICRATE] * cl.statsf[STAT_MOVEVARS_TIMESCALE] / bound(1, cl_netinputpacketsperserverpacket.value, 10);
+ cl.movevars_timescale = cl.statsf[STAT_MOVEVARS_TIMESCALE];
+ cl.movevars_gravity = cl.statsf[STAT_MOVEVARS_GRAVITY];
+ cl.movevars_stopspeed = cl.statsf[STAT_MOVEVARS_STOPSPEED] ;
+ cl.movevars_maxspeed = cl.statsf[STAT_MOVEVARS_MAXSPEED];
+ cl.movevars_spectatormaxspeed = cl.statsf[STAT_MOVEVARS_SPECTATORMAXSPEED];
+ cl.movevars_accelerate = cl.statsf[STAT_MOVEVARS_ACCELERATE];
+ cl.movevars_airaccelerate = cl.statsf[STAT_MOVEVARS_AIRACCELERATE];
+ cl.movevars_wateraccelerate = cl.statsf[STAT_MOVEVARS_WATERACCELERATE];
+ cl.movevars_entgravity = cl.statsf[STAT_MOVEVARS_ENTGRAVITY];
+ cl.movevars_jumpvelocity = cl.statsf[STAT_MOVEVARS_JUMPVELOCITY];
+ cl.movevars_edgefriction = cl.statsf[STAT_MOVEVARS_EDGEFRICTION];
+ cl.movevars_maxairspeed = cl.statsf[STAT_MOVEVARS_MAXAIRSPEED];
+ cl.movevars_stepheight = cl.statsf[STAT_MOVEVARS_STEPHEIGHT];
+ cl.movevars_airaccel_qw = cl.statsf[STAT_MOVEVARS_AIRACCEL_QW];
+ cl.movevars_airaccel_sideways_friction = cl.statsf[STAT_MOVEVARS_AIRACCEL_SIDEWAYS_FRICTION];
+ cl.movevars_friction = cl.statsf[STAT_MOVEVARS_FRICTION];
+ cl.movevars_wallfriction = cl.statsf[STAT_MOVEVARS_WALLFRICTION];
+ cl.movevars_waterfriction = cl.statsf[STAT_MOVEVARS_WATERFRICTION];
+ }
+ else
+ {
+ cl.movevars_packetinterval = slowmo.value / bound(1, cl_netinputpacketspersecond.value, 100);
+ cl.movevars_timescale = slowmo.value;
+ cl.movevars_gravity = sv_gravity.value;
+ cl.movevars_stopspeed = cl_movement_stopspeed.value;
+ cl.movevars_maxspeed = cl_movement_maxspeed.value;
+ cl.movevars_spectatormaxspeed = cl_movement_maxspeed.value;
+ cl.movevars_accelerate = cl_movement_accelerate.value;
+ cl.movevars_airaccelerate = cl_movement_airaccelerate.value < 0 ? cl_movement_accelerate.value : cl_movement_airaccelerate.value;
+ cl.movevars_wateraccelerate = cl_movement_wateraccelerate.value < 0 ? cl_movement_accelerate.value : cl_movement_wateraccelerate.value;
+ cl.movevars_friction = cl_movement_friction.value;
+ cl.movevars_wallfriction = cl_movement_wallfriction.value;
+ cl.movevars_waterfriction = cl_movement_waterfriction.value < 0 ? cl_movement_friction.value : cl_movement_waterfriction.value;
+ cl.movevars_entgravity = 1;
+ cl.movevars_jumpvelocity = cl_movement_jumpvelocity.value;
+ cl.movevars_edgefriction = cl_movement_edgefriction.value;
+ cl.movevars_maxairspeed = cl_movement_maxairspeed.value;
+ 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;
+ }
+}
+
+void CL_ClientMovement_Replay(void)
+{
+ int i;
+ qboolean canjump;
+ double totalmovetime;
+ cl_clientmovement_state_t s;
+
+ CL_ClientMovement_ExpireOldMoves();
+
+ // set up starting state for the series of moves
+ memset(&s, 0, sizeof(s));
+ VectorCopy(cl.entities[cl.playerentity].state_current.origin, s.origin);
+ VectorCopy(cl.mvelocity[0], s.velocity);
+ s.crouched = true; // will be updated on first move
+ //Con_Printf("movement replay starting org %f %f %f vel %f %f %f\n", s.origin[0], s.origin[1], s.origin[2], s.velocity[0], s.velocity[1], s.velocity[2]);
+
+ totalmovetime = 0;
+ for (i = 0;i < cl.movement_numqueue - 1;i++)
+ totalmovetime += cl.movement_queue[i].frametime;
+ cl.movement_predicted = totalmovetime * 1000.0 >= cl_movement_minping.value && cls.servermovesequence && (cl_movement.integer && !cls.demoplayback && cls.signon == SIGNONS && cl.stats[STAT_HEALTH] > 0 && !cl.intermission);
+ //Con_Printf("%i = %.0f >= %.0f && %i && (%i && %i && %i == %i && %i > 0 && %i\n", cl.movement_predicted, totalmovetime * 1000.0, cl_movement_minping.value, cls.servermovesequence, cl_movement.integer, !cls.demoplayback, cls.signon, SIGNONS, cl.stats[STAT_HEALTH], !cl.intermission);
+ if (cl.movement_predicted)
+ {
+ //Con_Printf("%ims\n", cl.movecmd[0].msec);
+
+ // replay the input queue to predict current location
+ // note: this relies on the fact there's always one queue item at the end
+
+ canjump = cl.movement_replay_canjump;
+ for (i = 0;i < cl.movement_numqueue;i++)
+ {
+ s.q = cl.movement_queue[i];
+ s.q.canjump = canjump;
+ // if a move is more than 50ms, do it as two moves (matching qwsv)
+ if (s.q.frametime > 0.05)