]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/commitdiff
Extract movement prediction into its own module and prepare for merge with server
authorTimePath <andrew.hardaker1995@gmail.com>
Mon, 8 Dec 2014 03:33:26 +0000 (14:33 +1100)
committerTimePath <andrew.hardaker1995@gmail.com>
Mon, 8 Dec 2014 03:33:26 +0000 (14:33 +1100)
qcsrc/client/progs.src
qcsrc/common/physics.qc [new file with mode: 0644]
qcsrc/csqcmodellib/cl_player.qc
qcsrc/server/cl_physics.qc
qcsrc/server/progs.src

index 86a8b43395e199d83a1dc1676f5a7d234cf1a27f..316cbf5acf6afcae50c6d1e27c942dee3003f85f 100644 (file)
@@ -122,6 +122,7 @@ command/cl_cmd.qc
 
 ../common/nades.qc
 ../common/buffs.qc
+../common/physics.qc
 
 ../warpzonelib/anglestransform.qc
 ../warpzonelib/mathlib.qc
diff --git a/qcsrc/common/physics.qc b/qcsrc/common/physics.qc
new file mode 100644 (file)
index 0000000..feb799b
--- /dev/null
@@ -0,0 +1,779 @@
+// TODO: water prediction
+
+// TODO: move to a common header
+#define VLEN2(v) dotproduct(v, v)
+
+// Client/server mappings
+#ifdef CSQC
+
+       #define PHYS_INPUT_ANGLES(s)                            input_angles
+       #define PHYS_INPUT_BUTTONS(s)                           input_buttons
+
+       #define PHYS_INPUT_TIMELENGTH                           input_timelength
+
+       #define PHYS_INPUT_MOVEVALUES(s)                        input_movevalues
+
+       #define GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE  moveflags & MOVEFLAG_GRAVITYUNAFFECTEDBYTICRATE
+       #define GAMEPLAYFIX_NOGRAVITYONGROUND                   moveflags & MOVEFLAG_NOGRAVITYONGROUND
+       #define GAMEPLAYFIX_Q2AIRACCELERATE                             moveflags & MOVEFLAG_Q2AIRACCELERATE
+
+       #define IS_DUCKED(s)                                            (s.pmove_flags & PMF_DUCKED)
+       #define SET_DUCKED(s)                                           s.pmove_flags |= PMF_DUCKED
+       #define UNSET_DUCKED(s)                                         s.pmove_flags &= ~PMF_DUCKED
+
+       #define IS_JUMP_HELD(s)                                         (s.pmove_flags & PMF_JUMP_HELD)
+       #define SET_JUMP_HELD(s)                                        s.pmove_flags |= PMF_JUMP_HELD
+       #define UNSET_JUMP_HELD(s)                                      s.pmove_flags &= ~PMF_JUMP_HELD
+
+       #define IS_ONGROUND(s)                                          (s.pmove_flags & PMF_ONGROUND)
+       #define SET_ONGROUND(s)                                         s.pmove_flags |= PMF_ONGROUND
+       #define UNSET_ONGROUND(s)                                       s.pmove_flags &= ~PMF_ONGROUND
+
+       #define PHYS_ACCELERATE                                         getstatf(STAT_MOVEVARS_ACCELERATE)
+       #define PHYS_AIRACCEL_QW                                        getstatf(STAT_MOVEVARS_AIRACCEL_QW)
+       #define PHYS_AIRACCEL_QW_STRETCHFACTOR          getstatf(STAT_MOVEVARS_AIRACCEL_QW_STRETCHFACTOR)
+       #define PHYS_AIRACCEL_SIDEWAYS_FRICTION         getstatf(STAT_MOVEVARS_AIRACCEL_SIDEWAYS_FRICTION)
+       #define PHYS_AIRACCELERATE                                      getstatf(STAT_MOVEVARS_AIRACCELERATE)
+       #define PHYS_AIRCONTROL                                         getstatf(STAT_MOVEVARS_AIRCONTROL)
+       #define PHYS_AIRCONTROL_PENALTY                         getstatf(STAT_MOVEVARS_AIRCONTROL_PENALTY)
+       #define PHYS_AIRCONTROL_POWER                           getstatf(STAT_MOVEVARS_AIRCONTROL_POWER)
+       #define PHYS_AIRSPEEDLIMIT_NONQW                        getstatf(STAT_MOVEVARS_AIRSPEEDLIMIT_NONQW)
+       #define PHYS_AIRSTOPACCELERATE                          getstatf(STAT_MOVEVARS_AIRSTOPACCELERATE)
+       #define PHYS_AIRSTRAFEACCEL_QW                          getstatf(STAT_MOVEVARS_AIRSTRAFEACCEL_QW)
+       #define PHYS_AIRSTRAFEACCELERATE                        getstatf(STAT_MOVEVARS_AIRSTRAFEACCELERATE)
+       #define PHYS_EDGEFRICTION                                       getstatf(STAT_MOVEVARS_EDGEFRICTION)
+       #define PHYS_ENTGRAVITY(s)                                      getstatf(STAT_MOVEVARS_ENTGRAVITY)
+       #define PHYS_FRICTION                                           getstatf(STAT_MOVEVARS_FRICTION)
+       #define PHYS_GRAVITY                                            getstatf(STAT_MOVEVARS_GRAVITY)
+       #define PHYS_JUMPVELOCITY                                       getstatf(STAT_MOVEVARS_JUMPVELOCITY)
+       #define PHYS_MAXAIRSPEED                                        getstatf(STAT_MOVEVARS_MAXAIRSPEED)
+       #define PHYS_MAXAIRSTRAFESPEED                          getstatf(STAT_MOVEVARS_MAXAIRSTRAFESPEED)
+       #define PHYS_MAXSPEED                                           getstatf(STAT_MOVEVARS_MAXSPEED)
+       #define PHYS_STEPHEIGHT                                         getstatf(STAT_MOVEVARS_STEPHEIGHT)
+       #define PHYS_STOPSPEED                                          getstatf(STAT_MOVEVARS_STOPSPEED)
+       #define PHYS_WARSOWBUNNY_ACCEL                          getstatf(STAT_MOVEVARS_WARSOWBUNNY_ACCEL)
+       #define PHYS_WARSOWBUNNY_BACKTOSIDERATIO        getstatf(STAT_MOVEVARS_WARSOWBUNNY_BACKTOSIDERATIO)
+       #define PHYS_WARSOWBUNNY_AIRFORWARDACCEL        getstatf(STAT_MOVEVARS_WARSOWBUNNY_AIRFORWARDACCEL)
+       #define PHYS_WARSOWBUNNY_TOPSPEED                       getstatf(STAT_MOVEVARS_WARSOWBUNNY_TOPSPEED)
+       #define PHYS_WARSOWBUNNY_TURNACCEL                      getstatf(STAT_MOVEVARS_WARSOWBUNNY_TURNACCEL)
+
+#elif defined(SVQC)
+
+       #define PHYS_INPUT_ANGLES(s)                            s.v_angle
+       // FIXME
+       #define PHYS_INPUT_BUTTONS(s)                           0
+
+       #define PHYS_INPUT_TIMELENGTH                           frametime
+
+       #define PHYS_INPUT_MOVEVALUES(s)                        s.movement
+
+       #define GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE  autocvar_sv_gameplayfix_gravityunaffectedbyticrate
+       #define GAMEPLAYFIX_NOGRAVITYONGROUND                   cvar("sv_gameplayfix_nogravityonground")
+       #define GAMEPLAYFIX_Q2AIRACCELERATE                             autocvar_sv_gameplayfix_q2airaccelerate
+
+       #define IS_DUCKED(s)                                            s.crouch
+       #define SET_DUCKED(s)                                           s.crouch = TRUE
+       #define UNSET_DUCKED(s)                                         s.crouch = FALSE
+
+       #define IS_JUMP_HELD(s)                                         (s.flags & FL_JUMPRELEASED == 0)
+       #define SET_JUMP_HELD(s)                                        s.flags &= ~FL_JUMPRELEASED
+       #define UNSET_JUMP_HELD(s)                                      s.flags |= FL_JUMPRELEASED
+
+       #define IS_ONGROUND(s)                                          (s.flags & FL_ONGROUND)
+       #define SET_ONGROUND(s)                                         s.flags |= FL_ONGROUND
+       #define UNSET_ONGROUND(s)                                       s.flags &= ~FL_ONGROUND
+
+       #define PHYS_ACCELERATE                                         autocvar_sv_accelerate
+       #define PHYS_AIRACCEL_QW                                        autocvar_sv_airaccel_qw
+       #define PHYS_AIRACCEL_QW_STRETCHFACTOR          autocvar_sv_airaccel_qw_stretchfactor
+       #define PHYS_AIRACCEL_SIDEWAYS_FRICTION         autocvar_sv_airaccel_sideways_friction
+       #define PHYS_AIRACCELERATE                                      autocvar_sv_airaccelerate
+       #define PHYS_AIRCONTROL                                         autocvar_sv_aircontrol
+       #define PHYS_AIRCONTROL_PENALTY                         autocvar_sv_aircontrol_penalty
+       #define PHYS_AIRCONTROL_POWER                           autocvar_sv_aircontrol_power
+       #define PHYS_AIRSPEEDLIMIT_NONQW                        autocvar_sv_airspeedlimit_nonqw
+       #define PHYS_AIRSTOPACCELERATE                          autocvar_sv_airstopaccelerate
+       #define PHYS_AIRSTRAFEACCEL_QW                          autocvar_sv_airstrafeaccel_qw
+       #define PHYS_AIRSTRAFEACCELERATE                        autocvar_sv_airstrafeaccelerate
+       #define PHYS_EDGEFRICTION                                       1
+       #define PHYS_ENTGRAVITY(s)                                      s.gravity
+       #define PHYS_FRICTION                                           autocvar_sv_friction
+       #define PHYS_GRAVITY                                            autocvar_sv_gravity
+       #define PHYS_JUMPVELOCITY                                       autocvar_sv_jumpvelocity
+       #define PHYS_MAXAIRSPEED                                        autocvar_sv_maxairspeed
+       #define PHYS_MAXAIRSTRAFESPEED                          autocvar_sv_maxairstrafespeed
+       #define PHYS_MAXSPEED                                           autocvar_sv_maxspeed
+       #define PHYS_STEPHEIGHT                                         autocvar_sv_stepheight
+       #define PHYS_STOPSPEED                                          autocvar_sv_stopspeed
+       #define PHYS_WARSOWBUNNY_ACCEL                          autocvar_sv_warsowbunny_accel
+       #define PHYS_WARSOWBUNNY_BACKTOSIDERATIO        autocvar_sv_warsowbunny_backtosideratio
+       #define PHYS_WARSOWBUNNY_AIRFORWARDACCEL        autocvar_sv_warsowbunny_airforwardaccel
+       #define PHYS_WARSOWBUNNY_TOPSPEED                       autocvar_sv_warsowbunny_topspeed
+       #define PHYS_WARSOWBUNNY_TURNACCEL                      autocvar_sv_warsowbunny_turnaccel
+
+#endif
+
+float IsMoveInDirection(vector mv, float angle) // key mix factor
+{
+       if(mv_x == 0 && mv_y == 0)
+               return 0; // avoid division by zero
+       angle -= RAD2DEG * atan2(mv_y, mv_x);
+       angle = remainder(angle, 360) / 45;
+       if(angle >  1)
+               return 0;
+       if(angle < -1)
+               return 0;
+       return 1 - fabs(angle);
+}
+
+float GeomLerp(float a, float lerp, float 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);
+}
+
+float pmove_waterjumptime; // weird engine flag we shouldn't really use but have to for now
+
+const float unstick_count = 27;
+vector unstick_offsets[unstick_count] =
+{
+// 1 no nudge (just return the original if this test passes)
+       '0.000   0.000  0.000',
+// 6 simple nudges
+       ' 0.000  0.000  0.125', '0.000  0.000 -0.125',
+       '-0.125  0.000  0.000', '0.125  0.000  0.000',
+       ' 0.000 -0.125  0.000', '0.000  0.125  0.000',
+// 4 diagonal flat nudges
+       '-0.125 -0.125  0.000', '0.125 -0.125  0.000',
+       '-0.125  0.125  0.000', '0.125  0.125  0.000',
+// 8 diagonal upward nudges
+       '-0.125  0.000  0.125', '0.125  0.000  0.125',
+       ' 0.000 -0.125  0.125', '0.000  0.125  0.125',
+       '-0.125 -0.125  0.125', '0.125 -0.125  0.125',
+       '-0.125  0.125  0.125', '0.125  0.125  0.125',
+// 8 diagonal downward nudges
+       '-0.125  0.000 -0.125', '0.125  0.000 -0.125',
+       ' 0.000 -0.125 -0.125', '0.000  0.125 -0.125',
+       '-0.125 -0.125 -0.125', '0.125 -0.125 -0.125',
+       '-0.125  0.125 -0.125', '0.125  0.125 -0.125',
+};
+
+void CSQC_ClientMovement_Unstick(entity s)
+{
+       float i;
+       vector neworigin;
+       for (i = 0; i < unstick_count; i++)
+       {
+               neworigin = unstick_offsets[i] + s.origin;
+               tracebox(neworigin, PL_CROUCH_MIN, PL_CROUCH_MAX, neworigin, MOVE_NORMAL, s);
+               if (!trace_startsolid)
+               {
+                       s.origin = neworigin;
+                       return;// true;
+               }
+       }
+}
+
+void CSQC_ClientMovement_UpdateStatus(entity s)
+{
+       float f;
+       vector origin1, origin2;
+
+       // make sure player is not stuck
+       CSQC_ClientMovement_Unstick(s);
+
+       // set crouched
+       if (PHYS_INPUT_BUTTONS(s) & 16)
+       {
+               // wants to crouch, this always works..
+               if (!IS_DUCKED(s))
+                       SET_DUCKED(s);
+       }
+       else
+       {
+               // wants to stand, if currently crouching we need to check for a
+               // low ceiling first
+               if (IS_DUCKED(s))
+               {
+                       tracebox(s.origin, PL_MIN, PL_MAX, s.origin, MOVE_NORMAL, s);
+                       if (!trace_startsolid)
+                               UNSET_DUCKED(s);
+               }
+       }
+       if (IS_DUCKED(s))
+       {
+               s.mins = PL_CROUCH_MIN;
+               s.maxs = PL_CROUCH_MAX;
+       }
+       else
+       {
+               s.mins = PL_MIN;
+               s.maxs = PL_MAX;
+       }
+
+       // set onground
+       origin1 = s.origin;
+       origin1_z += 1;
+       origin2 = s.origin;
+    origin2_z -= 1; // -2 causes clientside doublejump bug at above 150fps, raising that to 300fps :)
+
+       tracebox(origin1, s.mins, s.maxs, origin2, MOVE_NORMAL, s);
+       if(trace_fraction < 1 && trace_plane_normal_z > 0.7)
+       {
+               SET_ONGROUND(s);
+
+               // this code actually "predicts" an impact; so let's clip velocity first
+               f = dotproduct(s.velocity, trace_plane_normal);
+               if(f < 0) // only if moving downwards actually
+                       s.velocity -= f * trace_plane_normal;
+       }
+       else
+               UNSET_ONGROUND(s);
+
+       // set watertype/waterlevel
+       origin1 = s.origin;
+       origin1_z += s.mins_z + 1;
+       s.waterlevel = WATERLEVEL_NONE;
+       // TODO: convert
+//     s.watertype = CL_TracePoint(origin1, MOVE_NOMONSTERS, s, 0, true, false, NULL, false).startsupercontents & SUPERCONTENTS_LIQUIDSMASK;
+//     if (s.watertype)
+//     {
+//             s.waterlevel = WATERLEVEL_WETFEET;
+//             origin1[2] = s.origin[2] + (s.mins[2] + s.maxs[2]) * 0.5f;
+//             if (CL_TracePoint(origin1, MOVE_NOMONSTERS, s, 0, true, false, NULL, false).startsupercontents & SUPERCONTENTS_LIQUIDSMASK)
+//             {
+//                     s.waterlevel = WATERLEVEL_SWIMMING;
+//                     origin1[2] = s.origin[2] + 22;
+//                     if (CL_TracePoint(origin1, MOVE_NOMONSTERS, s, 0, true, false, NULL, false).startsupercontents & SUPERCONTENTS_LIQUIDSMASK)
+//                             s.waterlevel = WATERLEVEL_SUBMERGED;
+//             }
+//     }
+//
+//     // water jump prediction
+//     if (IS_ONGROUND(s) || s.velocity_z <= 0 || pmove_waterjumptime <= 0)
+//             pmove_waterjumptime = 0;
+}
+
+void CSQC_ClientMovement_Move(entity s)
+{
+       float bump;
+       float t;
+       float f;
+       vector neworigin;
+       vector currentorigin2;
+       vector neworigin2;
+       vector primalvelocity;
+       float old_trace1_fraction;
+       vector old_trace1_endpos;
+       vector old_trace1_plane_normal;
+       float old_trace2_fraction;
+       vector old_trace2_plane_normal;
+       CSQC_ClientMovement_UpdateStatus(s);
+       primalvelocity = s.velocity;
+       for (bump = 0, t = PHYS_INPUT_TIMELENGTH; bump < 8 && VLEN2(s.velocity) > 0; bump++)
+       {
+               neworigin = s.origin + t * s.velocity;
+               tracebox(s.origin, s.mins, s.maxs, neworigin, MOVE_NORMAL, s);
+               old_trace1_fraction = trace_fraction;
+               old_trace1_endpos = trace_endpos;
+               old_trace1_plane_normal = trace_plane_normal;
+               if (trace_fraction < 1 && trace_plane_normal_z == 0)
+               {
+                       // may be a step or wall, try stepping up
+                       // first move forward at a higher level
+                       currentorigin2 = s.origin;
+                       currentorigin2_z += PHYS_STEPHEIGHT;
+                       neworigin2 = neworigin;
+                       neworigin2_z = s.origin_z + PHYS_STEPHEIGHT;
+                       tracebox(currentorigin2, s.mins, s.maxs, neworigin2, MOVE_NORMAL, s);
+                       if (!trace_startsolid)
+                       {
+                               // then move down from there
+                               currentorigin2 = trace_endpos;
+                               neworigin2 = trace_endpos;
+                               neworigin2_z = s.origin_z;
+                               old_trace2_fraction = trace_fraction;
+                               old_trace2_plane_normal = trace_plane_normal;
+                               tracebox(currentorigin2, s.mins, s.maxs, neworigin2, MOVE_NORMAL, s);
+                               //Con_Printf("%f %f %f %f : %f %f %f %f : %f %f %f %f\n", trace.fraction, trace.endpos[0], trace.endpos[1], trace.endpos[2], trace2.fraction, trace2.endpos[0], trace2.endpos[1], trace2.endpos[2], trace3.fraction, trace3.endpos[0], trace3.endpos[1], trace3.endpos[2]);
+                               // accept the new trace if it made some progress
+                               if (fabs(trace_endpos_x - old_trace1_endpos_x) >= 0.03125 || fabs(trace_endpos_y - old_trace1_endpos_y) >= 0.03125)
+                               {
+                                       trace_fraction = old_trace2_fraction;
+                                       trace_endpos = trace_endpos;
+                                       trace_plane_normal = old_trace2_plane_normal;
+                               }
+                               else
+                               {
+                                       trace_fraction = old_trace1_fraction;
+                                       trace_endpos = old_trace1_endpos;
+                                       trace_plane_normal = old_trace1_plane_normal;
+                               }
+                       }
+               }
+
+               // check if it moved at all
+               if (trace_fraction >= 0.001)
+                       s.origin = trace_endpos;
+
+               // check if it moved all the way
+               if (trace_fraction == 1)
+                       break;
+
+               // this is only really needed for nogravityonground combined with gravityunaffectedbyticrate
+               // <LordHavoc> I'm pretty sure I commented it out solely because it seemed redundant
+               // this got commented out in a change that supposedly makes the code match QW better
+               // so if this is broken, maybe put it in an if(cls.protocol != PROTOCOL_QUAKEWORLD) block
+               if (trace_plane_normal_z > 0.7)
+                       SET_ONGROUND(s);
+
+               t -= t * trace_fraction;
+
+               f = dotproduct(s.velocity, trace_plane_normal);
+               s.velocity -= f * trace_plane_normal;
+       }
+       if (pmove_waterjumptime > 0)
+               s.velocity = primalvelocity;
+}
+
+void CSQC_ClientMovement_Physics_CPM_PM_Aircontrol(entity s, vector wishdir, float wishspeed)
+{
+       float zspeed, xyspeed, dot, k;
+
+#if 0
+       // this doesn't play well with analog input
+       if(s.movement_x == 0 || s.movement_y != 0)
+               return; // can't control movement if not moving forward or backward
+       k = 32;
+#else
+       k = 32 * (2 * IsMoveInDirection(PHYS_INPUT_MOVEVALUES(s), 0) - 1);
+       if(k <= 0)
+               return;
+#endif
+
+       k *= bound(0, wishspeed / PHYS_MAXAIRSPEED, 1);
+
+       zspeed = s.velocity_z;
+       s.velocity_z = 0;
+       xyspeed = vlen(s.velocity); s.velocity = normalize(s.velocity);
+
+       dot = s.velocity * wishdir;
+
+       if(dot > 0) // we can't change direction while slowing down
+       {
+               k *= pow(dot, PHYS_AIRCONTROL_POWER)*PHYS_INPUT_TIMELENGTH;
+               xyspeed = max(0, xyspeed - PHYS_AIRCONTROL_PENALTY * sqrt(max(0, 1 - dot*dot)) * k/32);
+               k *= PHYS_AIRCONTROL;
+               s.velocity = normalize(s.velocity * xyspeed + wishdir * k);
+       }
+
+       s.velocity = s.velocity * xyspeed;
+       s.velocity_z = zspeed;
+}
+
+float CSQC_ClientMovement_Physics_AdjustAirAccelQW(float accelqw, float factor)
+{
+       return copysign(bound(0.000001, 1 - (1 - fabs(accelqw)) * factor, 1), accelqw);
+}
+
+void CSQC_ClientMovement_Physics_PM_Accelerate(entity s, vector wishdir, float wishspeed, float wishspeed0, float accel, float accelqw, float stretchfactor, float sidefric, float speedlimit)
+{
+       float vel_straight;
+       float vel_z;
+       vector vel_perpend;
+       float step;
+       vector vel_xy;
+       float vel_xy_current;
+       float vel_xy_backward, vel_xy_forward;
+       float speedclamp;
+
+       if(stretchfactor > 0)
+               speedclamp = stretchfactor;
+       else if(accelqw < 0)
+               speedclamp = 1;
+       else
+               speedclamp = -1; // no clamping
+
+       if(accelqw < 0)
+               accelqw = -accelqw;
+
+       if(GAMEPLAYFIX_Q2AIRACCELERATE)
+               wishspeed0 = wishspeed; // don't need to emulate this Q1 bug
+
+       vel_straight = dotproduct(s.velocity, wishdir);
+       vel_z = s.velocity_z;
+       vel_xy = s.velocity;
+       vel_xy_z -= vel_z;
+       vel_perpend = vel_xy - vel_straight * wishdir;
+
+       step = accel * PHYS_INPUT_TIMELENGTH * wishspeed0;
+
+       vel_xy_current  = vlen(vel_xy);
+       if(speedlimit > 0)
+               accelqw = CSQC_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 && VLEN2(vel_perpend))
+               // negative: only apply so much sideways friction to stay below the speed you could get by "braking"
+       {
+               float f, fmin;
+               f = max(0, 1 + PHYS_INPUT_TIMELENGTH * wishspeed * sidefric);
+               fmin = (vel_xy_backward*vel_xy_backward - vel_straight*vel_straight) / VLEN2(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)
+                       vel_perpend *= f;
+               else
+               {
+                       fmin = sqrt(fmin);
+                       vel_perpend *= max(fmin, f);
+               }
+       }
+       else
+               vel_perpend *= max(0, 1 - PHYS_INPUT_TIMELENGTH * wishspeed * sidefric);
+
+       s.velocity = vel_perpend + vel_straight * wishdir;
+
+       if(speedclamp >= 0)
+       {
+               float vel_xy_preclamp;
+               vel_xy_preclamp = vlen(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)
+                               s.velocity *= (vel_xy_current / vel_xy_preclamp);
+               }
+       }
+
+       s.velocity_z += vel_z;
+}
+
+void CSQC_ClientMovement_Physics_PM_AirAccelerate(entity s, vector wishdir, float wishspeed)
+{
+       vector curvel, wishvel, acceldir, curdir;
+       float addspeed, accelspeed, curspeed, f;
+       float dot;
+
+       if(wishspeed == 0)
+               return;
+
+       curvel = s.velocity;
+       curvel_z = 0;
+       curspeed = vlen(curvel);
+
+       if(wishspeed > curspeed * 1.01)
+       {
+               wishspeed = min(wishspeed, curspeed + PHYS_WARSOWBUNNY_AIRFORWARDACCEL * PHYS_MAXSPEED * PHYS_INPUT_TIMELENGTH);
+       }
+       else
+       {
+               f = max(0, (PHYS_WARSOWBUNNY_TOPSPEED - curspeed) / (PHYS_WARSOWBUNNY_TOPSPEED - PHYS_MAXSPEED));
+               wishspeed = max(curspeed, PHYS_WARSOWBUNNY_ACCEL) + PHYS_WARSOWBUNNY_ACCEL * f * PHYS_WARSOWBUNNY_ACCEL * PHYS_INPUT_TIMELENGTH;
+       }
+       wishvel = wishdir * wishspeed;
+       acceldir = wishvel - curvel;
+       addspeed = vlen(acceldir);
+       acceldir = normalize(acceldir);
+
+       accelspeed = min(addspeed, PHYS_WARSOWBUNNY_TURNACCEL * PHYS_WARSOWBUNNY_ACCEL * PHYS_INPUT_TIMELENGTH);
+
+       if(PHYS_WARSOWBUNNY_BACKTOSIDERATIO < 1)
+       {
+               curdir = normalize(curvel);
+               dot = acceldir * curdir;
+               if(dot < 0)
+                       acceldir = acceldir - (1 - PHYS_WARSOWBUNNY_BACKTOSIDERATIO) * dot * curdir;
+       }
+
+       s.velocity += accelspeed * acceldir;
+}
+
+void CSQC_ClientMovement_Physics_Walk(entity s)
+{
+       float friction;
+       float wishspeed;
+       float addspeed;
+       float accelspeed;
+       float f;
+       float g;
+       vector wishvel;
+       vector wishdir;
+       vector yawangles;
+
+       // jump if on ground with jump button pressed but only if it has been
+       // released at least once since the last jump
+       if (PHYS_INPUT_BUTTONS(s) & 2)
+       {
+               if (IS_ONGROUND(s) && (!IS_JUMP_HELD(s) || !cvar("cl_movement_track_canjump")))
+               {
+                       s.velocity_z += PHYS_JUMPVELOCITY;
+                       UNSET_ONGROUND(s);
+                       SET_JUMP_HELD(s); // canjump = false
+               }
+       }
+       else
+               UNSET_JUMP_HELD(s); // canjump = true
+
+       // calculate movement vector
+       yawangles = '0 0 0';
+       yawangles_y = PHYS_INPUT_ANGLES(s)_y;
+       makevectors(yawangles);
+       wishvel = PHYS_INPUT_MOVEVALUES(s)_x * v_forward + PHYS_INPUT_MOVEVALUES(s)_y * v_right;
+
+       // split wishvel into wishspeed and wishdir
+       wishspeed = vlen(wishvel);
+       if (wishspeed)
+               wishdir = wishvel / wishspeed;
+       else
+               wishdir = '0 0 0';
+       // check if onground
+       if (IS_ONGROUND(s))
+       {
+               wishspeed = min(wishspeed, PHYS_MAXSPEED);
+               if (IS_DUCKED(s))
+                       wishspeed *= 0.5;
+
+               // apply edge friction
+               f = sqrt(s.velocity_x * s.velocity_x + s.velocity_y * s.velocity_y);
+               if (f > 0)
+               {
+                       friction = PHYS_FRICTION;
+                       if (PHYS_EDGEFRICTION != 1)
+                       {
+                               vector neworigin2;
+                               vector neworigin3;
+                               // note: QW uses the full player box for the trace, and yet still
+                               // uses s.origin_z + s.mins_z, which is clearly an bug, but
+                               // this mimics it for compatibility
+                               neworigin2 = s.origin;
+                               neworigin2_x += s.velocity_x*(16/f);
+                               neworigin2_y += s.velocity_y*(16/f);
+                               neworigin2_z += s.mins_z;
+                               neworigin3 = neworigin2;
+                               neworigin3_z -= 34;
+                               traceline(neworigin2, neworigin3, MOVE_NORMAL, s);
+                               if (trace_fraction == 1 && !trace_startsolid)
+                                       friction *= PHYS_EDGEFRICTION;
+                       }
+                       // apply ground friction
+                       f = 1 - PHYS_INPUT_TIMELENGTH * friction * ((f < PHYS_STOPSPEED) ? (PHYS_STOPSPEED / f) : 1);
+                       f = max(f, 0);
+                       s.velocity *= f;
+               }
+               addspeed = wishspeed - dotproduct(s.velocity, wishdir);
+               if (addspeed > 0)
+               {
+                       accelspeed = min(PHYS_ACCELERATE * PHYS_INPUT_TIMELENGTH * wishspeed, addspeed);
+                       s.velocity += accelspeed * wishdir;
+               }
+               g = PHYS_GRAVITY * PHYS_ENTGRAVITY(s) * PHYS_INPUT_TIMELENGTH;
+               if(!(GAMEPLAYFIX_NOGRAVITYONGROUND))
+               {
+                       if(GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE)
+                               s.velocity_z -= g * 0.5;
+                       else
+                               s.velocity_z -= g;
+               }
+               if (VLEN2(s.velocity))
+                       CSQC_ClientMovement_Move(s);
+               if(!(GAMEPLAYFIX_NOGRAVITYONGROUND) || !IS_ONGROUND(s))
+               {
+                       if(GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE)
+                               s.velocity_z -= g * 0.5;
+               }
+       }
+       else
+       {
+               if (pmove_waterjumptime <= 0)
+               {
+                       // apply air speed limit
+                       float accel, wishspeed0, wishspeed2, accelqw, strafity;
+                       float accelerating;
+
+                       accelqw = PHYS_AIRACCEL_QW;
+                       wishspeed0 = wishspeed;
+                       wishspeed = min(wishspeed, PHYS_MAXAIRSPEED);
+                       if (IS_DUCKED(s))
+                               wishspeed *= 0.5;
+                       accel = PHYS_AIRACCELERATE;
+
+                       accelerating = (dotproduct(s.velocity, wishdir) > 0);
+                       wishspeed2 = wishspeed;
+
+                       // CPM: air control
+                       if(PHYS_AIRSTOPACCELERATE != 0)
+                       {
+                               vector curdir;
+                               curdir_x = s.velocity_x;
+                               curdir_y = s.velocity_y;
+                               curdir_z = 0;
+                               curdir = normalize(curdir);
+                               accel = accel + (PHYS_AIRSTOPACCELERATE - accel) * max(0, -dotproduct(curdir, wishdir));
+                       }
+                       strafity = IsMoveInDirection(PHYS_INPUT_MOVEVALUES(s), -90) + IsMoveInDirection(PHYS_INPUT_MOVEVALUES(s), +90); // if one is nonzero, other is always zero
+                       if(PHYS_MAXAIRSTRAFESPEED)
+                               wishspeed = min(wishspeed, GeomLerp(PHYS_MAXAIRSPEED, strafity, PHYS_MAXAIRSTRAFESPEED));
+                       if(PHYS_AIRSTRAFEACCELERATE)
+                               accel = GeomLerp(PHYS_AIRACCELERATE, strafity, PHYS_AIRSTRAFEACCELERATE);
+                       if(PHYS_AIRSTRAFEACCEL_QW)
+                               accelqw =
+                                       (((strafity > 0.5 ? PHYS_AIRSTRAFEACCEL_QW : PHYS_AIRACCEL_QW) >= 0) ? +1 : -1)
+                                       *
+                                       (1 - GeomLerp(1 - fabs(PHYS_AIRACCEL_QW), strafity, 1 - fabs(PHYS_AIRSTRAFEACCEL_QW)));
+                       // !CPM
+
+                       if(PHYS_WARSOWBUNNY_TURNACCEL && accelerating && PHYS_INPUT_MOVEVALUES(s)_y == 0 && PHYS_INPUT_MOVEVALUES(s)_x != 0)
+                               CSQC_ClientMovement_Physics_PM_AirAccelerate(s, wishdir, wishspeed2);
+                       else
+                               CSQC_ClientMovement_Physics_PM_Accelerate(s, wishdir, wishspeed, wishspeed0, accel, accelqw, PHYS_AIRACCEL_QW_STRETCHFACTOR, PHYS_AIRACCEL_SIDEWAYS_FRICTION / PHYS_MAXAIRSPEED, PHYS_AIRSPEEDLIMIT_NONQW);
+
+                       if(PHYS_AIRCONTROL)
+                               CSQC_ClientMovement_Physics_CPM_PM_Aircontrol(s, wishdir, wishspeed2);
+               }
+               g = PHYS_GRAVITY * PHYS_ENTGRAVITY(s) * PHYS_INPUT_TIMELENGTH;
+               if(GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE)
+                       s.velocity_z -= g * 0.5;
+               else
+                       s.velocity_z -= g;
+               CSQC_ClientMovement_Move(s);
+               if(!(GAMEPLAYFIX_NOGRAVITYONGROUND) || !IS_ONGROUND(s))
+               {
+                       if(GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE)
+                               s.velocity_z -= g * 0.5;
+               }
+       }
+}
+
+// TODO: merge this with main physics frame
+void CSQC_ClientMovement_Physics_Swim(entity s)
+{
+       // swimming
+       self.flags &= ~FL_ONGROUND;
+
+       makevectors(PHYS_INPUT_ANGLES(s));
+       //wishvel = v_forward * self.movement_x + v_right * self.movement_y + v_up * self.movement_z;
+       vector wishvel = v_forward * PHYS_INPUT_MOVEVALUES(s)_x + v_right * PHYS_INPUT_MOVEVALUES(s)_y + '0 0 1' * PHYS_INPUT_MOVEVALUES(s)_z;
+       if (wishvel == '0 0 0')
+               wishvel = '0 0 -60'; // drift towards bottom
+
+       vector wishdir = normalize(wishvel);
+       float wishspeed = vlen(wishvel);
+       if (wishspeed > PHYS_MAXSPEED)
+               wishspeed = PHYS_MAXSPEED;
+       wishspeed = wishspeed * 0.7;
+
+       // water friction
+       self.velocity = self.velocity * (1 - PHYS_INPUT_TIMELENGTH * PHYS_FRICTION);
+
+       // water acceleration
+       CSQC_ClientMovement_Physics_PM_Accelerate(s, wishdir, wishspeed, wishspeed, PHYS_ACCELERATE, 1, 0, 0, 0);
+}
+
+void CSQC_ClientMovement_PlayerMove(entity s)
+{
+       //Con_Printf(" %f", frametime);
+       if (!(PHYS_INPUT_BUTTONS(s) & 2)) // !jump
+               UNSET_JUMP_HELD(s); // canjump = true
+       pmove_waterjumptime -= PHYS_INPUT_TIMELENGTH;
+       CSQC_ClientMovement_UpdateStatus(s);
+       if (s.waterlevel >= WATERLEVEL_SWIMMING)
+               CSQC_ClientMovement_Physics_Swim(s);
+       else
+               CSQC_ClientMovement_Physics_Walk(s);
+}
+
+void CSQC_ClientMovement_PlayerMove_Frame(entity s)
+{
+       // if a move is more than 50ms, do it as two moves (matching qwsv)
+       //Con_Printf("%i ", s.cmd.msec);
+       if(PHYS_INPUT_TIMELENGTH > 0.0005)
+       {
+               if (PHYS_INPUT_TIMELENGTH > 0.05)
+               {
+                       PHYS_INPUT_TIMELENGTH /= 2;
+                       CSQC_ClientMovement_PlayerMove(s);
+               }
+               CSQC_ClientMovement_PlayerMove(s);
+       }
+       else
+       {
+               // we REALLY need this handling to happen, even if the move is not executed
+               if (!(PHYS_INPUT_BUTTONS(s) & 2)) // !jump
+                       UNSET_JUMP_HELD(s); // canjump = true
+       }
+}
+
+#undef VLEN2
+
+#undef PHYS_INPUT_ANGLES
+#undef PHYS_INPUT_BUTTONS
+
+#undef PHYS_INPUT_TIMELENGTH
+
+#undef PHYS_INPUT_MOVEVALUES
+
+#undef GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE
+#undef GAMEPLAYFIX_NOGRAVITYONGROUND
+#undef GAMEPLAYFIX_Q2AIRACCELERATE
+
+#undef IS_DUCKED
+#undef SET_DUCKED
+#undef UNSET_DUCKED
+
+#undef IS_JUMP_HELD
+#undef SET_JUMP_HELD
+#undef UNSET_JUMP_HELD
+
+#undef IS_ONGROUND
+#undef SET_ONGROUND
+#undef UNSET_ONGROUND
+
+#undef PHYS_ACCELERATE
+#undef PHYS_AIRACCEL_QW
+#undef PHYS_AIRACCEL_QW_STRETCHFACTOR
+#undef PHYS_AIRACCEL_SIDEWAYS_FRICTION
+#undef PHYS_AIRACCELERATE
+#undef PHYS_AIRCONTROL
+#undef PHYS_AIRCONTROL_PENALTY
+#undef PHYS_AIRCONTROL_POWER
+#undef PHYS_AIRSPEEDLIMIT_NONQW
+#undef PHYS_AIRSTOPACCELERATE
+#undef PHYS_AIRSTRAFEACCEL_QW
+#undef PHYS_AIRSTRAFEACCELERATE
+#undef PHYS_EDGEFRICTION
+#undef PHYS_ENTGRAVITY
+#undef PHYS_FRICTION
+#undef PHYS_GRAVITY
+#undef PHYS_JUMPVELOCITY
+#undef PHYS_MAXAIRSPEED
+#undef PHYS_MAXAIRSTRAFESPEED
+#undef PHYS_MAXSPEED
+#undef PHYS_STEPHEIGHT
+#undef PHYS_STOPSPEED
+#undef PHYS_WARSOWBUNNY_ACCEL
+#undef PHYS_WARSOWBUNNY_BACKTOSIDERATIO
+#undef PHYS_WARSOWBUNNY_AIRFORWARDACCEL
+#undef PHYS_WARSOWBUNNY_TOPSPEED
+#undef PHYS_WARSOWBUNNY_TURNACCEL
\ No newline at end of file
index 4b848e9b7ce46395726bd5ddbf614356c28c2f0b..1310c43ad7e8e4b5b41221ec4fdd81a4b2fac0b9 100644 (file)
@@ -120,641 +120,7 @@ void CSQCPlayer_SavePrediction()
        csqcplayer_status = CSQCPLAYERSTATUS_PREDICTED;
 }
 
-// TODO: water prediction
-float pmove_waterjumptime; // weird engine flag we shouldn't really use but have to for now
-// TODO: move to a common header
-#define vlen2(v) dotproduct(v, v)
-
-const float unstick_count = 27;
-vector unstick_offsets[unstick_count] =
-{
-// 1 no nudge (just return the original if this test passes)
-       '0.000   0.000  0.000',
-// 6 simple nudges
-       ' 0.000  0.000  0.125', '0.000  0.000 -0.125',
-       '-0.125  0.000  0.000', '0.125  0.000  0.000',
-       ' 0.000 -0.125  0.000', '0.000  0.125  0.000',
-// 4 diagonal flat nudges
-       '-0.125 -0.125  0.000', '0.125 -0.125  0.000',
-       '-0.125  0.125  0.000', '0.125  0.125  0.000',
-// 8 diagonal upward nudges
-       '-0.125  0.000  0.125', '0.125  0.000  0.125',
-       ' 0.000 -0.125  0.125', '0.000  0.125  0.125',
-       '-0.125 -0.125  0.125', '0.125 -0.125  0.125',
-       '-0.125  0.125  0.125', '0.125  0.125  0.125',
-// 8 diagonal downward nudges
-       '-0.125  0.000 -0.125', '0.125  0.000 -0.125',
-       ' 0.000 -0.125 -0.125', '0.000  0.125 -0.125',
-       '-0.125 -0.125 -0.125', '0.125 -0.125 -0.125',
-       '-0.125  0.125 -0.125', '0.125  0.125 -0.125',
-};
-
-float CSQC_ClientMovement_Unstick(entity s)
-{
-       float i;
-       vector neworigin;
-       for (i = 0; i < unstick_count; i++)
-       {
-               neworigin = unstick_offsets[i] + s.origin;
-               tracebox(neworigin, PL_CROUCH_MIN, PL_CROUCH_MAX, neworigin, MOVE_NORMAL, s);
-               if (!trace_startsolid)
-               {
-                       s.origin = neworigin;
-                       return true;
-               }
-       }
-       // if all offsets failed, give up
-       return false;
-}
-
-void CSQC_ClientMovement_UpdateStatus(entity s)
-{
-       float f;
-       vector origin1, origin2;
-
-       // make sure player is not stuck
-       CSQC_ClientMovement_Unstick(s);
-
-       // set crouched
-       if (input_buttons & 16)
-       {
-               // wants to crouch, this always works..
-               if (!s.pmove_flags & PMF_DUCKED)
-                       s.pmove_flags |= PMF_DUCKED;
-       }
-       else
-       {
-               // wants to stand, if currently crouching we need to check for a
-               // low ceiling first
-               if (s.pmove_flags & PMF_DUCKED)
-               {
-                       tracebox(s.origin, PL_MIN, PL_MAX, s.origin, MOVE_NORMAL, s);
-                       if (!trace_startsolid)
-                               s.pmove_flags &= ~PMF_DUCKED;
-               }
-       }
-       if (s.pmove_flags & PMF_DUCKED)
-       {
-               s.mins = PL_CROUCH_MIN;
-               s.maxs = PL_CROUCH_MAX;
-       }
-       else
-       {
-               s.mins = PL_MIN;
-               s.maxs = PL_MAX;
-       }
-
-       // set onground
-       origin1 = s.origin;
-       origin1_z += 1;
-       origin2 = s.origin;
-    origin2_z -= 1; // -2 causes clientside doublejump bug at above 150fps, raising that to 300fps :)
-
-       tracebox(origin1, s.mins, s.maxs, origin2, MOVE_NORMAL, s);
-       if(trace_fraction < 1 && trace_plane_normal_z > 0.7)
-       {
-               s.pmove_flags |= PMF_ONGROUND;
-
-               // this code actually "predicts" an impact; so let's clip velocity first
-               f = dotproduct(s.velocity, trace_plane_normal);
-               if(f < 0) // only if moving downwards actually
-                       s.velocity -= f * trace_plane_normal;
-       }
-       else
-               s.pmove_flags &= ~PMF_ONGROUND; // onground = false;
-
-       // set watertype/waterlevel
-       origin1 = s.origin;
-       origin1_z += s.mins_z + 1;
-       s.waterlevel = WATERLEVEL_NONE;
-       // TODO: convert
-//     s.watertype = CL_TracePoint(origin1, MOVE_NOMONSTERS, s, 0, true, false, NULL, false).startsupercontents & SUPERCONTENTS_LIQUIDSMASK;
-//     if (s.watertype)
-//     {
-//             s.waterlevel = WATERLEVEL_WETFEET;
-//             origin1[2] = s.origin[2] + (s.mins[2] + s.maxs[2]) * 0.5f;
-//             if (CL_TracePoint(origin1, MOVE_NOMONSTERS, s, 0, true, false, NULL, false).startsupercontents & SUPERCONTENTS_LIQUIDSMASK)
-//             {
-//                     s.waterlevel = WATERLEVEL_SWIMMING;
-//                     origin1[2] = s.origin[2] + 22;
-//                     if (CL_TracePoint(origin1, MOVE_NOMONSTERS, s, 0, true, false, NULL, false).startsupercontents & SUPERCONTENTS_LIQUIDSMASK)
-//                             s.waterlevel = WATERLEVEL_SUBMERGED;
-//             }
-//     }
-//
-//     // water jump prediction
-//     if ((s.pmove_flags & PMF_ONGROUND) || s.velocity_z <= 0 || pmove_waterjumptime <= 0)
-//             pmove_waterjumptime = 0;
-}
-
-void CSQC_ClientMovement_Move(entity s)
-{
-       float bump;
-       float t;
-       float f;
-       vector neworigin;
-       vector currentorigin2;
-       vector neworigin2;
-       vector primalvelocity;
-       float old_trace1_fraction;
-       vector old_trace1_endpos;
-       vector old_trace1_plane_normal;
-       float old_trace2_fraction;
-       vector old_trace2_plane_normal;
-       CSQC_ClientMovement_UpdateStatus(s);
-       primalvelocity = s.velocity;
-       for (bump = 0, t = input_timelength; bump < 8 && vlen2(s.velocity) > 0; bump++)
-       {
-               neworigin = s.origin + t * s.velocity;
-               tracebox(s.origin, s.mins, s.maxs, neworigin, MOVE_NORMAL, s);
-               old_trace1_fraction = trace_fraction;
-               old_trace1_endpos = trace_endpos;
-               old_trace1_plane_normal = trace_plane_normal;
-               if (trace_fraction < 1 && trace_plane_normal_z == 0)
-               {
-                       // may be a step or wall, try stepping up
-                       // first move forward at a higher level
-                       currentorigin2 = s.origin;
-                       currentorigin2_z += getstatf(STAT_MOVEVARS_STEPHEIGHT);
-                       neworigin2 = neworigin;
-                       neworigin2_z = s.origin_z + getstatf(STAT_MOVEVARS_STEPHEIGHT);
-                       tracebox(currentorigin2, s.mins, s.maxs, neworigin2, MOVE_NORMAL, s);
-                       if (!trace_startsolid)
-                       {
-                               // then move down from there
-                               currentorigin2 = trace_endpos;
-                               neworigin2 = trace_endpos;
-                               neworigin2_z = s.origin_z;
-                               old_trace2_fraction = trace_fraction;
-                               old_trace2_plane_normal = trace_plane_normal;
-                               tracebox(currentorigin2, s.mins, s.maxs, neworigin2, MOVE_NORMAL, s);
-                               //Con_Printf("%f %f %f %f : %f %f %f %f : %f %f %f %f\n", trace.fraction, trace.endpos[0], trace.endpos[1], trace.endpos[2], trace2.fraction, trace2.endpos[0], trace2.endpos[1], trace2.endpos[2], trace3.fraction, trace3.endpos[0], trace3.endpos[1], trace3.endpos[2]);
-                               // accept the new trace if it made some progress
-                               if (fabs(trace_endpos_x - old_trace1_endpos_x) >= 0.03125 || fabs(trace_endpos_y - old_trace1_endpos_y) >= 0.03125)
-                               {
-                                       trace_fraction = old_trace2_fraction;
-                                       trace_endpos = trace_endpos;
-                                       trace_plane_normal = old_trace2_plane_normal;
-                               }
-                               else
-                               {
-                                       trace_fraction = old_trace1_fraction;
-                                       trace_endpos = old_trace1_endpos;
-                                       trace_plane_normal = old_trace1_plane_normal;
-                               }
-                       }
-               }
-
-               // check if it moved at all
-               if (trace_fraction >= 0.001)
-                       s.origin = trace_endpos;
-
-               // check if it moved all the way
-               if (trace_fraction == 1)
-                       break;
-
-               // this is only really needed for nogravityonground combined with gravityunaffectedbyticrate
-               // <LordHavoc> I'm pretty sure I commented it out solely because it seemed redundant
-               // this got commented out in a change that supposedly makes the code match QW better
-               // so if this is broken, maybe put it in an if(cls.protocol != PROTOCOL_QUAKEWORLD) block
-               if (trace_plane_normal_z > 0.7)
-                       s.pmove_flags |= PMF_ONGROUND;
-
-               t -= t * trace_fraction;
-
-               f = dotproduct(s.velocity, trace_plane_normal);
-               s.velocity -= f * trace_plane_normal;
-       }
-       if (pmove_waterjumptime > 0)
-               s.velocity = primalvelocity;
-}
-
-float IsMoveInDirection(vector mv, float angle) // key mix factor
-{
-       if(mv_x == 0 && mv_y == 0)
-               return 0; // avoid division by zero
-       angle -= RAD2DEG * atan2(mv_y, mv_x);
-       angle = remainder(angle, 360) / 45;
-       if(angle >  1)
-               return 0;
-       if(angle < -1)
-               return 0;
-       return 1 - fabs(angle);
-}
-
-// TODO: remove this and use above function
-float CSQC_IsMoveInDirection(float forward, float side, float angle)
-{
-       // TODO: move to a common header
-       #define RAD2DEG(a) ((a) * (180.0f / M_PI))
-       #define ANGLEMOD(a) ((a) - 360.0 * floor((a) / 360.0))
-       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);
-       #undef RAD2DEG
-       #undef ANGLEMOD
-}
-
-float GeomLerp(float a, float lerp, float 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 CSQC_ClientMovement_Physics_CPM_PM_Aircontrol(entity s, vector wishdir, float wishspeed)
-{
-       float zspeed, xyspeed, dot, k;
-
-#if 0
-       // this doesn't play well with analog input
-       if(s.movement_x == 0 || s.movement_y != 0)
-               return; // can't control movement if not moving forward or backward
-       k = 32;
-#else
-       k = 32 * (2 * IsMoveInDirection(input_movevalues, 0) - 1);
-       if(k <= 0)
-               return;
-#endif
-
-       k *= bound(0, wishspeed / getstatf(STAT_MOVEVARS_MAXAIRSPEED), 1);
-
-       zspeed = s.velocity_z;
-       s.velocity_z = 0;
-       xyspeed = vlen(s.velocity); s.velocity = normalize(s.velocity);
-
-       dot = s.velocity * wishdir;
-
-       if(dot > 0) // we can't change direction while slowing down
-       {
-               k *= pow(dot, getstatf(STAT_MOVEVARS_AIRCONTROL_POWER))*input_timelength;
-               xyspeed = max(0, xyspeed - getstatf(STAT_MOVEVARS_AIRCONTROL_PENALTY) * sqrt(max(0, 1 - dot*dot)) * k/32);
-               k *= getstatf(STAT_MOVEVARS_AIRCONTROL);
-               s.velocity = normalize(s.velocity * xyspeed + wishdir * k);
-       }
-
-       s.velocity = s.velocity * xyspeed;
-       s.velocity_z = zspeed;
-}
-
-float CSQC_ClientMovement_Physics_AdjustAirAccelQW(float accelqw, float factor)
-{
-       return copysign(bound(0.000001, 1 - (1 - fabs(accelqw)) * factor, 1), accelqw);
-}
-
-void CSQC_ClientMovement_Physics_PM_Accelerate(entity s, vector wishdir, float wishspeed, float wishspeed0, float accel, float accelqw, float stretchfactor, float sidefric, float speedlimit)
-{
-       float vel_straight;
-       float vel_z;
-       vector vel_perpend;
-       float step;
-       vector vel_xy;
-       float vel_xy_current;
-       float vel_xy_backward, vel_xy_forward;
-       float speedclamp;
-
-       if(stretchfactor > 0)
-               speedclamp = stretchfactor;
-       else if(accelqw < 0)
-               speedclamp = 1;
-       else
-               speedclamp = -1; // no clamping
-
-       if(accelqw < 0)
-               accelqw = -accelqw;
-
-       if(moveflags & MOVEFLAG_Q2AIRACCELERATE)
-               wishspeed0 = wishspeed; // don't need to emulate this Q1 bug
-
-       vel_straight = dotproduct(s.velocity, wishdir);
-       vel_z = s.velocity_z;
-       vel_xy = s.velocity;
-       vel_xy_z -= vel_z;
-       vel_perpend = vel_xy - vel_straight * wishdir;
-
-       step = accel * input_timelength * wishspeed0;
-
-       vel_xy_current  = vlen(vel_xy);
-       if(speedlimit > 0)
-               accelqw = CSQC_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 && vlen2(vel_perpend))
-               // negative: only apply so much sideways friction to stay below the speed you could get by "braking"
-       {
-               float f, fmin;
-               f = max(0, 1 + input_timelength * wishspeed * sidefric);
-               fmin = (vel_xy_backward*vel_xy_backward - vel_straight*vel_straight) / vlen2(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)
-                       vel_perpend *= f;
-               else
-               {
-                       fmin = sqrt(fmin);
-                       vel_perpend *= max(fmin, f);
-               }
-       }
-       else
-               vel_perpend *= max(0, 1 - input_timelength * wishspeed * sidefric);
-
-       s.velocity = vel_perpend + vel_straight * wishdir;
-
-       if(speedclamp >= 0)
-       {
-               float vel_xy_preclamp;
-               vel_xy_preclamp = vlen(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)
-                               s.velocity *= (vel_xy_current / vel_xy_preclamp);
-               }
-       }
-
-       s.velocity_z += vel_z;
-}
-
-void CSQC_ClientMovement_Physics_PM_AirAccelerate(entity s, vector wishdir, float wishspeed)
-{
-       vector curvel, wishvel, acceldir, curdir;
-       float addspeed, accelspeed, curspeed, f;
-       float dot;
-
-       if(wishspeed == 0)
-               return;
-
-       curvel = s.velocity;
-       curvel_z = 0;
-       curspeed = vlen(curvel);
-
-       if(wishspeed > curspeed * 1.01)
-       {
-               wishspeed = min(wishspeed, curspeed + getstatf(STAT_MOVEVARS_WARSOWBUNNY_AIRFORWARDACCEL) * getstatf(STAT_MOVEVARS_MAXSPEED) * input_timelength);
-       }
-       else
-       {
-               f = max(0, (getstatf(STAT_MOVEVARS_WARSOWBUNNY_TOPSPEED) - curspeed) / (getstatf(STAT_MOVEVARS_WARSOWBUNNY_TOPSPEED) - getstatf(STAT_MOVEVARS_MAXSPEED)));
-               wishspeed = max(curspeed, getstatf(STAT_MOVEVARS_WARSOWBUNNY_ACCEL)) + getstatf(STAT_MOVEVARS_WARSOWBUNNY_ACCEL) * f * getstatf(STAT_MOVEVARS_WARSOWBUNNY_ACCEL) * input_timelength;
-       }
-       wishvel = wishdir * wishspeed;
-       acceldir = wishvel - curvel;
-       addspeed = vlen(acceldir);
-       acceldir = normalize(acceldir);
-
-       accelspeed = min(addspeed, getstatf(STAT_MOVEVARS_WARSOWBUNNY_TURNACCEL) * getstatf(STAT_MOVEVARS_WARSOWBUNNY_ACCEL) * input_timelength);
-
-       if(getstatf(STAT_MOVEVARS_WARSOWBUNNY_BACKTOSIDERATIO) < 1)
-       {
-               curdir = normalize(curvel);
-               dot = acceldir * curdir;
-               if(dot < 0)
-                       acceldir = acceldir - (1 - getstatf(STAT_MOVEVARS_WARSOWBUNNY_BACKTOSIDERATIO)) * dot * curdir;
-       }
-
-       s.velocity += accelspeed * acceldir;
-}
-
-void CSQC_ClientMovement_Physics_Walk(entity s)
-{
-       float friction;
-       float wishspeed;
-       float addspeed;
-       float accelspeed;
-       float f;
-       float gravity;
-       vector wishvel;
-       vector wishdir;
-       vector yawangles;
-
-       // jump if on ground with jump button pressed but only if it has been
-       // released at least once since the last jump
-       if (input_buttons & 2)
-       {
-               if ((s.pmove_flags & PMF_ONGROUND) && ((s.pmove_flags & PMF_JUMP_HELD) == 0 || !cvar("cl_movement_track_canjump")))
-               {
-                       s.velocity_z += getstatf(STAT_MOVEVARS_JUMPVELOCITY);
-                       s.pmove_flags &= ~PMF_ONGROUND;
-                       s.pmove_flags |= PMF_JUMP_HELD; // canjump = false
-               }
-       }
-       else
-               s.pmove_flags &= ~PMF_JUMP_HELD; // canjump = true
-
-       // calculate movement vector
-       yawangles = '0 0 0';
-       yawangles_y = input_angles_y;
-       makevectors(yawangles);
-       wishvel = input_movevalues_x * v_forward + input_movevalues_y * v_right;
-
-       // split wishvel into wishspeed and wishdir
-       wishspeed = vlen(wishvel);
-       if (wishspeed)
-               wishdir = wishvel / wishspeed;
-       else
-               wishdir = '0 0 0';
-       // check if onground
-       if ((s.pmove_flags & PMF_ONGROUND))
-       {
-               wishspeed = min(wishspeed, getstatf(STAT_MOVEVARS_MAXSPEED));
-               if (s.pmove_flags & PMF_DUCKED)
-                       wishspeed *= 0.5;
-
-               // apply edge friction
-               f = sqrt(s.velocity_x * s.velocity_x + s.velocity_y * s.velocity_y);
-               if (f > 0)
-               {
-                       friction = getstatf(STAT_MOVEVARS_FRICTION);
-                       if (getstatf(STAT_MOVEVARS_EDGEFRICTION) != 1)
-                       {
-                               vector neworigin2;
-                               vector neworigin3;
-                               // note: QW uses the full player box for the trace, and yet still
-                               // uses s.origin_z + s.mins_z, which is clearly an bug, but
-                               // this mimics it for compatibility
-                               neworigin2 = s.origin;
-                               neworigin2_x += s.velocity_x*(16/f);
-                               neworigin2_y += s.velocity_y*(16/f);
-                               neworigin2_z += s.mins_z;
-                               neworigin3 = neworigin2;
-                               neworigin3_z -= 34;
-                               traceline(neworigin2, neworigin3, MOVE_NORMAL, s);
-                               if (trace_fraction == 1 && !trace_startsolid)
-                                       friction *= getstatf(STAT_MOVEVARS_EDGEFRICTION);
-                       }
-                       // apply ground friction
-                       f = 1 - input_timelength * friction * ((f < getstatf(STAT_MOVEVARS_STOPSPEED)) ? (getstatf(STAT_MOVEVARS_STOPSPEED) / f) : 1);
-                       f = max(f, 0);
-                       s.velocity *= f;
-               }
-               addspeed = wishspeed - dotproduct(s.velocity, wishdir);
-               if (addspeed > 0)
-               {
-                       accelspeed = min(getstatf(STAT_MOVEVARS_ACCELERATE) * input_timelength * wishspeed, addspeed);
-                       s.velocity += accelspeed * wishdir;
-               }
-               gravity = getstatf(STAT_MOVEVARS_GRAVITY) * getstatf(STAT_MOVEVARS_ENTGRAVITY) * input_timelength;
-               if(!(moveflags & MOVEFLAG_NOGRAVITYONGROUND))
-               {
-                       if(moveflags & MOVEFLAG_GRAVITYUNAFFECTEDBYTICRATE)
-                               s.velocity_z -= gravity * 0.5;
-                       else
-                               s.velocity_z -= gravity;
-               }
-               if (vlen2(s.velocity))
-                       CSQC_ClientMovement_Move(s);
-               if(!(moveflags & MOVEFLAG_NOGRAVITYONGROUND) || !(s.pmove_flags & PMF_ONGROUND))
-               {
-                       if(moveflags & MOVEFLAG_GRAVITYUNAFFECTEDBYTICRATE)
-                               s.velocity_z -= gravity * 0.5;
-               }
-       }
-       else
-       {
-               if (pmove_waterjumptime <= 0)
-               {
-                       // apply air speed limit
-                       float accel, wishspeed0, wishspeed2, accelqw, strafity;
-                       float accelerating;
-
-                       accelqw = getstatf(STAT_MOVEVARS_AIRACCEL_QW);
-                       wishspeed0 = wishspeed;
-                       wishspeed = min(wishspeed, getstatf(STAT_MOVEVARS_MAXAIRSPEED));
-                       if (s.pmove_flags & PMF_DUCKED)
-                               wishspeed *= 0.5;
-                       accel = getstatf(STAT_MOVEVARS_AIRACCELERATE);
-
-                       accelerating = (dotproduct(s.velocity, wishdir) > 0);
-                       wishspeed2 = wishspeed;
-
-                       // CPM: air control
-                       if(getstatf(STAT_MOVEVARS_AIRSTOPACCELERATE) != 0)
-                       {
-                               vector curdir;
-                               curdir_x = s.velocity_x;
-                               curdir_y = s.velocity_y;
-                               curdir_z = 0;
-                               curdir = normalize(curdir);
-                               accel = accel + (getstatf(STAT_MOVEVARS_AIRSTOPACCELERATE) - accel) * max(0, -dotproduct(curdir, wishdir));
-                       }
-                       strafity = CSQC_IsMoveInDirection(input_movevalues_x, input_movevalues_y, -90) + CSQC_IsMoveInDirection(input_movevalues_x, input_movevalues_y, +90); // if one is nonzero, other is always zero
-                       if(getstatf(STAT_MOVEVARS_MAXAIRSTRAFESPEED))
-                               wishspeed = min(wishspeed, GeomLerp(getstatf(STAT_MOVEVARS_MAXAIRSPEED), strafity, getstatf(STAT_MOVEVARS_MAXAIRSTRAFESPEED)));
-                       if(getstatf(STAT_MOVEVARS_AIRSTRAFEACCELERATE))
-                               accel = GeomLerp(getstatf(STAT_MOVEVARS_AIRACCELERATE), strafity, getstatf(STAT_MOVEVARS_AIRSTRAFEACCELERATE));
-                       if(getstatf(STAT_MOVEVARS_AIRSTRAFEACCEL_QW))
-                               accelqw =
-                                       (((strafity > 0.5 ? getstatf(STAT_MOVEVARS_AIRSTRAFEACCEL_QW) : getstatf(STAT_MOVEVARS_AIRACCEL_QW)) >= 0) ? +1 : -1)
-                                       *
-                                       (1 - GeomLerp(1 - fabs(getstatf(STAT_MOVEVARS_AIRACCEL_QW)), strafity, 1 - fabs(getstatf(STAT_MOVEVARS_AIRSTRAFEACCEL_QW))));
-                       // !CPM
-
-                       if(getstatf(STAT_MOVEVARS_WARSOWBUNNY_TURNACCEL) && accelerating && input_movevalues_y == 0 && input_movevalues_x != 0)
-                               CSQC_ClientMovement_Physics_PM_AirAccelerate(s, wishdir, wishspeed2);
-                       else
-                               CSQC_ClientMovement_Physics_PM_Accelerate(s, wishdir, wishspeed, wishspeed0, accel, accelqw, getstatf(STAT_MOVEVARS_AIRACCEL_QW_STRETCHFACTOR), getstatf(STAT_MOVEVARS_AIRACCEL_SIDEWAYS_FRICTION) / getstatf(STAT_MOVEVARS_MAXAIRSPEED), getstatf(STAT_MOVEVARS_AIRSPEEDLIMIT_NONQW));
-
-                       if(getstatf(STAT_MOVEVARS_AIRCONTROL))
-                               CSQC_ClientMovement_Physics_CPM_PM_Aircontrol(s, wishdir, wishspeed2);
-               }
-               gravity = getstatf(STAT_MOVEVARS_GRAVITY) * getstatf(STAT_MOVEVARS_ENTGRAVITY) * input_timelength;
-               if(moveflags & MOVEFLAG_GRAVITYUNAFFECTEDBYTICRATE)
-                       s.velocity_z -= gravity * 0.5;
-               else
-                       s.velocity_z -= gravity;
-               CSQC_ClientMovement_Move(s);
-               if(!(moveflags & MOVEFLAG_NOGRAVITYONGROUND) || !(s.pmove_flags & PMF_ONGROUND))
-               {
-                       if(moveflags & MOVEFLAG_GRAVITYUNAFFECTEDBYTICRATE)
-                               s.velocity_z -= gravity * 0.5;
-               }
-       }
-}
-
-// TODO: merge this with main physics frame
-void CSQC_ClientMovement_Physics_Swim(entity s)
-{
-       // swimming
-       self.flags &= ~FL_ONGROUND;
-
-       makevectors(input_angles);
-       //wishvel = v_forward * self.movement_x + v_right * self.movement_y + v_up * self.movement_z;
-       vector wishvel = v_forward * input_movevalues_x + v_right * input_movevalues_y + '0 0 1' * input_movevalues_z;
-       if (wishvel == '0 0 0')
-               wishvel = '0 0 -60'; // drift towards bottom
-
-       vector wishdir = normalize(wishvel);
-       float wishspeed = vlen(wishvel);
-       if (wishspeed > getstatf(STAT_MOVEVARS_MAXSPEED))
-               wishspeed = getstatf(STAT_MOVEVARS_MAXSPEED);
-       wishspeed = wishspeed * 0.7;
-
-       // water friction
-       self.velocity = self.velocity * (1 - input_timelength * getstatf(STAT_MOVEVARS_FRICTION));
-
-       // water acceleration
-       CSQC_ClientMovement_Physics_PM_Accelerate(s, wishdir, wishspeed, wishspeed, getstatf(STAT_MOVEVARS_ACCELERATE), 1, 0, 0, 0);
-}
-
-void CSQC_ClientMovement_PlayerMove(entity s)
-{
-       //Con_Printf(" %f", frametime);
-       if (!(input_buttons & 2)) // !jump
-               s.pmove_flags &= ~PMF_JUMP_HELD; // canjump = true
-       pmove_waterjumptime -= input_timelength;
-       CSQC_ClientMovement_UpdateStatus(s);
-       if (s.waterlevel >= WATERLEVEL_SWIMMING)
-               CSQC_ClientMovement_Physics_Swim(s);
-       else
-               CSQC_ClientMovement_Physics_Walk(s);
-}
-
-void CSQC_ClientMovement_PlayerMove_Frame(entity s)
-{
-       // if a move is more than 50ms, do it as two moves (matching qwsv)
-       //Con_Printf("%i ", s.cmd.msec);
-       if(input_timelength > 0.0005)
-       {
-               if (input_timelength > 0.05)
-               {
-                       input_timelength /= 2;
-                       CSQC_ClientMovement_PlayerMove(s);
-               }
-               CSQC_ClientMovement_PlayerMove(s);
-       }
-       else
-       {
-               // we REALLY need this handling to happen, even if the move is not executed
-               if (!(input_buttons & 2)) // !jump
-                       s.pmove_flags &= ~PMF_JUMP_HELD; // canjump = true
-       }
-}
+void CSQC_ClientMovement_PlayerMove_Frame(entity s);
 
 void CSQCPlayer_Physics(void)
 {
@@ -764,7 +130,6 @@ void CSQCPlayer_Physics(void)
                case 2: CSQC_ClientMovement_PlayerMove_Frame(self); break;
        }
 }
-#undef vlen2
 
 void CSQCPlayer_PredictTo(float endframe, float apply_error)
 {
index 14747fa99aabe535d9074e7e03ae6391adcebf80..fbff30684e23bb8cd39ccb2448fcc4db3d368f7e 100644 (file)
@@ -387,37 +387,9 @@ void RaceCarPhysics()
        self.angles_z =  smoothangles_z;
 }
 
-float IsMoveInDirection(vector mv, float angle) // key mix factor
-{
-       if(mv_x == 0 && mv_y == 0)
-               return 0; // avoid division by zero
-       angle -= RAD2DEG * atan2(mv_y, mv_x);
-       angle = remainder(angle, 360) / 45;
-       if(angle >  1)
-               return 0;
-       if(angle < -1)
-               return 0;
-       return 1 - fabs(angle);
-}
+float IsMoveInDirection(vector mv, float angle);
 
-float GeomLerp(float a, float lerp, float 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);
-}
+float GeomLerp(float a, float lerp, float b);
 
 void CPM_PM_Aircontrol(vector wishdir, float wishspeed)
 {
index 288b2477597c00114d0a3b0246c296f3e1360b51..d84b37b943a8667ee9e708573ea19c7b6226a620 100644 (file)
@@ -213,7 +213,7 @@ target_music.qc
 
 ../common/nades.qc
 ../common/buffs.qc
-
+../common/physics.qc
 
 accuracy.qc
 ../csqcmodellib/sv_model.qc