+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 stretchfactor, vec_t sidefric, vec_t speedlimit)
+{
+ vec_t vel_straight;
+ vec_t vel_z;
+ vec3_t vel_perpend;
+ vec_t step;
+ vec3_t vel_xy;
+ vec_t vel_xy_current;
+ vec_t vel_xy_backward, vel_xy_forward;
+ vec_t speedclamp;
+
+ if(stretchfactor > 0)
+ speedclamp = stretchfactor;
+ else if(accelqw < 0)
+ speedclamp = 1;
+ else
+ speedclamp = -1; // no clamping
+
+ if(accelqw < 0)
+ accelqw = -accelqw;
+
+ if(cl.moveflags & MOVEFLAG_Q2AIRACCELERATE)
+ wishspeed0 = wishspeed; // don't need to emulate this Q1 bug
+
+ vel_straight = DotProduct(s->velocity, wishdir);
+ vel_z = s->velocity[2];
+ VectorCopy(s->velocity, vel_xy); vel_xy[2] -= vel_z;
+ VectorMA(vel_xy, -vel_straight, wishdir, vel_perpend);
+
+ 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)
+ vel_xy_backward = 0; // not that it REALLY occurs that this would cause wrong behaviour afterwards
+
+ vel_straight = vel_straight + bound(0, wishspeed - vel_straight, step) * accelqw + step * (1 - accelqw);
+
+ if(sidefric < 0 && VectorLength2(vel_perpend))
+ // negative: only apply so much sideways friction to stay below the speed you could get by "braking"
+ {
+ vec_t f, fmin;
+ 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
+ {
+ fmin = sqrt(fmin);
+ VectorScale(vel_perpend, max(fmin, f), vel_perpend);
+ }
+ }
+ else
+ VectorScale(vel_perpend, max(0, 1 - s->cmd.frametime * wishspeed * sidefric), vel_perpend);
+
+ VectorMA(vel_perpend, vel_straight, wishdir, s->velocity);
+
+ if(speedclamp >= 0)
+ {
+ vec_t vel_xy_preclamp;
+ vel_xy_preclamp = VectorLength(s->velocity);
+ if(vel_xy_preclamp > 0) // prevent division by zero
+ {
+ vel_xy_current += (vel_xy_forward - vel_xy_current) * speedclamp;
+ if(vel_xy_current < vel_xy_preclamp)
+ VectorScale(s->velocity, (vel_xy_current / vel_xy_preclamp), 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 );
+}
+