1 // TODO: water prediction
3 // TODO: move to a common header
4 #define VLEN2(v) dotproduct(v, v)
6 // Client/server mappings
9 #define PHYS_INPUT_ANGLES(s) input_angles
10 #define PHYS_INPUT_BUTTONS(s) input_buttons
12 #define PHYS_INPUT_TIMELENGTH input_timelength
14 #define PHYS_INPUT_MOVEVALUES(s) input_movevalues
16 #define GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE moveflags & MOVEFLAG_GRAVITYUNAFFECTEDBYTICRATE
17 #define GAMEPLAYFIX_NOGRAVITYONGROUND moveflags & MOVEFLAG_NOGRAVITYONGROUND
18 #define GAMEPLAYFIX_Q2AIRACCELERATE moveflags & MOVEFLAG_Q2AIRACCELERATE
20 #define IS_DUCKED(s) (s.pmove_flags & PMF_DUCKED)
21 #define SET_DUCKED(s) s.pmove_flags |= PMF_DUCKED
22 #define UNSET_DUCKED(s) s.pmove_flags &= ~PMF_DUCKED
24 #define IS_JUMP_HELD(s) (s.pmove_flags & PMF_JUMP_HELD)
25 #define SET_JUMP_HELD(s) s.pmove_flags |= PMF_JUMP_HELD
26 #define UNSET_JUMP_HELD(s) s.pmove_flags &= ~PMF_JUMP_HELD
28 #define IS_ONGROUND(s) (s.pmove_flags & PMF_ONGROUND)
29 #define SET_ONGROUND(s) s.pmove_flags |= PMF_ONGROUND
30 #define UNSET_ONGROUND(s) s.pmove_flags &= ~PMF_ONGROUND
32 #define PHYS_ACCELERATE getstatf(STAT_MOVEVARS_ACCELERATE)
33 #define PHYS_AIRACCEL_QW getstatf(STAT_MOVEVARS_AIRACCEL_QW)
34 #define PHYS_AIRACCEL_QW_STRETCHFACTOR getstatf(STAT_MOVEVARS_AIRACCEL_QW_STRETCHFACTOR)
35 #define PHYS_AIRACCEL_SIDEWAYS_FRICTION getstatf(STAT_MOVEVARS_AIRACCEL_SIDEWAYS_FRICTION)
36 #define PHYS_AIRACCELERATE getstatf(STAT_MOVEVARS_AIRACCELERATE)
37 #define PHYS_AIRCONTROL getstatf(STAT_MOVEVARS_AIRCONTROL)
38 #define PHYS_AIRCONTROL_PENALTY getstatf(STAT_MOVEVARS_AIRCONTROL_PENALTY)
39 #define PHYS_AIRCONTROL_POWER getstatf(STAT_MOVEVARS_AIRCONTROL_POWER)
40 #define PHYS_AIRSPEEDLIMIT_NONQW getstatf(STAT_MOVEVARS_AIRSPEEDLIMIT_NONQW)
41 #define PHYS_AIRSTOPACCELERATE getstatf(STAT_MOVEVARS_AIRSTOPACCELERATE)
42 #define PHYS_AIRSTRAFEACCEL_QW getstatf(STAT_MOVEVARS_AIRSTRAFEACCEL_QW)
43 #define PHYS_AIRSTRAFEACCELERATE getstatf(STAT_MOVEVARS_AIRSTRAFEACCELERATE)
44 #define PHYS_EDGEFRICTION getstatf(STAT_MOVEVARS_EDGEFRICTION)
45 #define PHYS_ENTGRAVITY(s) getstatf(STAT_MOVEVARS_ENTGRAVITY)
46 #define PHYS_FRICTION getstatf(STAT_MOVEVARS_FRICTION)
47 #define PHYS_GRAVITY getstatf(STAT_MOVEVARS_GRAVITY)
48 #define PHYS_JUMPVELOCITY getstatf(STAT_MOVEVARS_JUMPVELOCITY)
49 #define PHYS_MAXAIRSPEED getstatf(STAT_MOVEVARS_MAXAIRSPEED)
50 #define PHYS_MAXAIRSTRAFESPEED getstatf(STAT_MOVEVARS_MAXAIRSTRAFESPEED)
51 #define PHYS_MAXSPEED getstatf(STAT_MOVEVARS_MAXSPEED)
52 #define PHYS_STEPHEIGHT getstatf(STAT_MOVEVARS_STEPHEIGHT)
53 #define PHYS_STOPSPEED getstatf(STAT_MOVEVARS_STOPSPEED)
54 #define PHYS_WARSOWBUNNY_ACCEL getstatf(STAT_MOVEVARS_WARSOWBUNNY_ACCEL)
55 #define PHYS_WARSOWBUNNY_BACKTOSIDERATIO getstatf(STAT_MOVEVARS_WARSOWBUNNY_BACKTOSIDERATIO)
56 #define PHYS_WARSOWBUNNY_AIRFORWARDACCEL getstatf(STAT_MOVEVARS_WARSOWBUNNY_AIRFORWARDACCEL)
57 #define PHYS_WARSOWBUNNY_TOPSPEED getstatf(STAT_MOVEVARS_WARSOWBUNNY_TOPSPEED)
58 #define PHYS_WARSOWBUNNY_TURNACCEL getstatf(STAT_MOVEVARS_WARSOWBUNNY_TURNACCEL)
62 #define PHYS_INPUT_ANGLES(s) s.v_angle
64 #define PHYS_INPUT_BUTTONS(s) 0
66 #define PHYS_INPUT_TIMELENGTH frametime
68 #define PHYS_INPUT_MOVEVALUES(s) s.movement
70 #define GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE autocvar_sv_gameplayfix_gravityunaffectedbyticrate
71 #define GAMEPLAYFIX_NOGRAVITYONGROUND cvar("sv_gameplayfix_nogravityonground")
72 #define GAMEPLAYFIX_Q2AIRACCELERATE autocvar_sv_gameplayfix_q2airaccelerate
74 #define IS_DUCKED(s) s.crouch
75 #define SET_DUCKED(s) s.crouch = TRUE
76 #define UNSET_DUCKED(s) s.crouch = FALSE
78 #define IS_JUMP_HELD(s) (s.flags & FL_JUMPRELEASED == 0)
79 #define SET_JUMP_HELD(s) s.flags &= ~FL_JUMPRELEASED
80 #define UNSET_JUMP_HELD(s) s.flags |= FL_JUMPRELEASED
82 #define IS_ONGROUND(s) (s.flags & FL_ONGROUND)
83 #define SET_ONGROUND(s) s.flags |= FL_ONGROUND
84 #define UNSET_ONGROUND(s) s.flags &= ~FL_ONGROUND
86 #define PHYS_ACCELERATE autocvar_sv_accelerate
87 #define PHYS_AIRACCEL_QW autocvar_sv_airaccel_qw
88 #define PHYS_AIRACCEL_QW_STRETCHFACTOR autocvar_sv_airaccel_qw_stretchfactor
89 #define PHYS_AIRACCEL_SIDEWAYS_FRICTION autocvar_sv_airaccel_sideways_friction
90 #define PHYS_AIRACCELERATE autocvar_sv_airaccelerate
91 #define PHYS_AIRCONTROL autocvar_sv_aircontrol
92 #define PHYS_AIRCONTROL_PENALTY autocvar_sv_aircontrol_penalty
93 #define PHYS_AIRCONTROL_POWER autocvar_sv_aircontrol_power
94 #define PHYS_AIRSPEEDLIMIT_NONQW autocvar_sv_airspeedlimit_nonqw
95 #define PHYS_AIRSTOPACCELERATE autocvar_sv_airstopaccelerate
96 #define PHYS_AIRSTRAFEACCEL_QW autocvar_sv_airstrafeaccel_qw
97 #define PHYS_AIRSTRAFEACCELERATE autocvar_sv_airstrafeaccelerate
98 #define PHYS_EDGEFRICTION 1
99 #define PHYS_ENTGRAVITY(s) s.gravity
100 #define PHYS_FRICTION autocvar_sv_friction
101 #define PHYS_GRAVITY autocvar_sv_gravity
102 #define PHYS_JUMPVELOCITY autocvar_sv_jumpvelocity
103 #define PHYS_MAXAIRSPEED autocvar_sv_maxairspeed
104 #define PHYS_MAXAIRSTRAFESPEED autocvar_sv_maxairstrafespeed
105 #define PHYS_MAXSPEED autocvar_sv_maxspeed
106 #define PHYS_STEPHEIGHT autocvar_sv_stepheight
107 #define PHYS_STOPSPEED autocvar_sv_stopspeed
108 #define PHYS_WARSOWBUNNY_ACCEL autocvar_sv_warsowbunny_accel
109 #define PHYS_WARSOWBUNNY_BACKTOSIDERATIO autocvar_sv_warsowbunny_backtosideratio
110 #define PHYS_WARSOWBUNNY_AIRFORWARDACCEL autocvar_sv_warsowbunny_airforwardaccel
111 #define PHYS_WARSOWBUNNY_TOPSPEED autocvar_sv_warsowbunny_topspeed
112 #define PHYS_WARSOWBUNNY_TURNACCEL autocvar_sv_warsowbunny_turnaccel
116 float IsMoveInDirection(vector mv, float angle) // key mix factor
118 if(mv_x == 0 && mv_y == 0)
119 return 0; // avoid division by zero
120 angle -= RAD2DEG * atan2(mv_y, mv_x);
121 angle = remainder(angle, 360) / 45;
126 return 1 - fabs(angle);
129 float GeomLerp(float a, float lerp, float b)
145 return a * pow(fabs(b / a), lerp);
148 float pmove_waterjumptime; // weird engine flag we shouldn't really use but have to for now
150 const float unstick_count = 27;
151 vector unstick_offsets[unstick_count] =
153 // 1 no nudge (just return the original if this test passes)
156 ' 0.000 0.000 0.125', '0.000 0.000 -0.125',
157 '-0.125 0.000 0.000', '0.125 0.000 0.000',
158 ' 0.000 -0.125 0.000', '0.000 0.125 0.000',
159 // 4 diagonal flat nudges
160 '-0.125 -0.125 0.000', '0.125 -0.125 0.000',
161 '-0.125 0.125 0.000', '0.125 0.125 0.000',
162 // 8 diagonal upward nudges
163 '-0.125 0.000 0.125', '0.125 0.000 0.125',
164 ' 0.000 -0.125 0.125', '0.000 0.125 0.125',
165 '-0.125 -0.125 0.125', '0.125 -0.125 0.125',
166 '-0.125 0.125 0.125', '0.125 0.125 0.125',
167 // 8 diagonal downward nudges
168 '-0.125 0.000 -0.125', '0.125 0.000 -0.125',
169 ' 0.000 -0.125 -0.125', '0.000 0.125 -0.125',
170 '-0.125 -0.125 -0.125', '0.125 -0.125 -0.125',
171 '-0.125 0.125 -0.125', '0.125 0.125 -0.125',
174 void CSQC_ClientMovement_Unstick(entity s)
178 for (i = 0; i < unstick_count; i++)
180 neworigin = unstick_offsets[i] + s.origin;
181 tracebox(neworigin, PL_CROUCH_MIN, PL_CROUCH_MAX, neworigin, MOVE_NORMAL, s);
182 if (!trace_startsolid)
184 s.origin = neworigin;
190 void CSQC_ClientMovement_UpdateStatus(entity s)
193 vector origin1, origin2;
195 // make sure player is not stuck
196 CSQC_ClientMovement_Unstick(s);
199 if (PHYS_INPUT_BUTTONS(s) & 16)
201 // wants to crouch, this always works..
207 // wants to stand, if currently crouching we need to check for a
211 tracebox(s.origin, PL_MIN, PL_MAX, s.origin, MOVE_NORMAL, s);
212 if (!trace_startsolid)
218 s.mins = PL_CROUCH_MIN;
219 s.maxs = PL_CROUCH_MAX;
231 origin2_z -= 1; // -2 causes clientside doublejump bug at above 150fps, raising that to 300fps :)
233 tracebox(origin1, s.mins, s.maxs, origin2, MOVE_NORMAL, s);
234 if(trace_fraction < 1 && trace_plane_normal_z > 0.7)
238 // this code actually "predicts" an impact; so let's clip velocity first
239 f = dotproduct(s.velocity, trace_plane_normal);
240 if(f < 0) // only if moving downwards actually
241 s.velocity -= f * trace_plane_normal;
246 // set watertype/waterlevel
248 origin1_z += s.mins_z + 1;
249 s.waterlevel = WATERLEVEL_NONE;
251 // s.watertype = CL_TracePoint(origin1, MOVE_NOMONSTERS, s, 0, true, false, NULL, false).startsupercontents & SUPERCONTENTS_LIQUIDSMASK;
254 // s.waterlevel = WATERLEVEL_WETFEET;
255 // origin1[2] = s.origin[2] + (s.mins[2] + s.maxs[2]) * 0.5f;
256 // if (CL_TracePoint(origin1, MOVE_NOMONSTERS, s, 0, true, false, NULL, false).startsupercontents & SUPERCONTENTS_LIQUIDSMASK)
258 // s.waterlevel = WATERLEVEL_SWIMMING;
259 // origin1[2] = s.origin[2] + 22;
260 // if (CL_TracePoint(origin1, MOVE_NOMONSTERS, s, 0, true, false, NULL, false).startsupercontents & SUPERCONTENTS_LIQUIDSMASK)
261 // s.waterlevel = WATERLEVEL_SUBMERGED;
265 // // water jump prediction
266 // if (IS_ONGROUND(s) || s.velocity_z <= 0 || pmove_waterjumptime <= 0)
267 // pmove_waterjumptime = 0;
270 void CSQC_ClientMovement_Move(entity s)
276 vector currentorigin2;
278 vector primalvelocity;
279 float old_trace1_fraction;
280 vector old_trace1_endpos;
281 vector old_trace1_plane_normal;
282 float old_trace2_fraction;
283 vector old_trace2_plane_normal;
284 CSQC_ClientMovement_UpdateStatus(s);
285 primalvelocity = s.velocity;
286 for (bump = 0, t = PHYS_INPUT_TIMELENGTH; bump < 8 && VLEN2(s.velocity) > 0; bump++)
288 neworigin = s.origin + t * s.velocity;
289 tracebox(s.origin, s.mins, s.maxs, neworigin, MOVE_NORMAL, s);
290 old_trace1_fraction = trace_fraction;
291 old_trace1_endpos = trace_endpos;
292 old_trace1_plane_normal = trace_plane_normal;
293 if (trace_fraction < 1 && trace_plane_normal_z == 0)
295 // may be a step or wall, try stepping up
296 // first move forward at a higher level
297 currentorigin2 = s.origin;
298 currentorigin2_z += PHYS_STEPHEIGHT;
299 neworigin2 = neworigin;
300 neworigin2_z = s.origin_z + PHYS_STEPHEIGHT;
301 tracebox(currentorigin2, s.mins, s.maxs, neworigin2, MOVE_NORMAL, s);
302 if (!trace_startsolid)
304 // then move down from there
305 currentorigin2 = trace_endpos;
306 neworigin2 = trace_endpos;
307 neworigin2_z = s.origin_z;
308 old_trace2_fraction = trace_fraction;
309 old_trace2_plane_normal = trace_plane_normal;
310 tracebox(currentorigin2, s.mins, s.maxs, neworigin2, MOVE_NORMAL, s);
311 //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]);
312 // accept the new trace if it made some progress
313 if (fabs(trace_endpos_x - old_trace1_endpos_x) >= 0.03125 || fabs(trace_endpos_y - old_trace1_endpos_y) >= 0.03125)
315 trace_fraction = old_trace2_fraction;
316 trace_endpos = trace_endpos;
317 trace_plane_normal = old_trace2_plane_normal;
321 trace_fraction = old_trace1_fraction;
322 trace_endpos = old_trace1_endpos;
323 trace_plane_normal = old_trace1_plane_normal;
328 // check if it moved at all
329 if (trace_fraction >= 0.001)
330 s.origin = trace_endpos;
332 // check if it moved all the way
333 if (trace_fraction == 1)
336 // this is only really needed for nogravityonground combined with gravityunaffectedbyticrate
337 // <LordHavoc> I'm pretty sure I commented it out solely because it seemed redundant
338 // this got commented out in a change that supposedly makes the code match QW better
339 // so if this is broken, maybe put it in an if(cls.protocol != PROTOCOL_QUAKEWORLD) block
340 if (trace_plane_normal_z > 0.7)
343 t -= t * trace_fraction;
345 f = dotproduct(s.velocity, trace_plane_normal);
346 s.velocity -= f * trace_plane_normal;
348 if (pmove_waterjumptime > 0)
349 s.velocity = primalvelocity;
352 void CSQC_ClientMovement_Physics_CPM_PM_Aircontrol(entity s, vector wishdir, float wishspeed)
354 float zspeed, xyspeed, dot, k;
357 // this doesn't play well with analog input
358 if(s.movement_x == 0 || s.movement_y != 0)
359 return; // can't control movement if not moving forward or backward
362 k = 32 * (2 * IsMoveInDirection(PHYS_INPUT_MOVEVALUES(s), 0) - 1);
367 k *= bound(0, wishspeed / PHYS_MAXAIRSPEED, 1);
369 zspeed = s.velocity_z;
371 xyspeed = vlen(s.velocity); s.velocity = normalize(s.velocity);
373 dot = s.velocity * wishdir;
375 if(dot > 0) // we can't change direction while slowing down
377 k *= pow(dot, PHYS_AIRCONTROL_POWER)*PHYS_INPUT_TIMELENGTH;
378 xyspeed = max(0, xyspeed - PHYS_AIRCONTROL_PENALTY * sqrt(max(0, 1 - dot*dot)) * k/32);
379 k *= PHYS_AIRCONTROL;
380 s.velocity = normalize(s.velocity * xyspeed + wishdir * k);
383 s.velocity = s.velocity * xyspeed;
384 s.velocity_z = zspeed;
387 float CSQC_ClientMovement_Physics_AdjustAirAccelQW(float accelqw, float factor)
389 return copysign(bound(0.000001, 1 - (1 - fabs(accelqw)) * factor, 1), accelqw);
392 void CSQC_ClientMovement_Physics_PM_Accelerate(entity s, vector wishdir, float wishspeed, float wishspeed0, float accel, float accelqw, float stretchfactor, float sidefric, float speedlimit)
399 float vel_xy_current;
400 float vel_xy_backward, vel_xy_forward;
403 if(stretchfactor > 0)
404 speedclamp = stretchfactor;
408 speedclamp = -1; // no clamping
413 if(GAMEPLAYFIX_Q2AIRACCELERATE)
414 wishspeed0 = wishspeed; // don't need to emulate this Q1 bug
416 vel_straight = dotproduct(s.velocity, wishdir);
417 vel_z = s.velocity_z;
420 vel_perpend = vel_xy - vel_straight * wishdir;
422 step = accel * PHYS_INPUT_TIMELENGTH * wishspeed0;
424 vel_xy_current = vlen(vel_xy);
426 accelqw = CSQC_ClientMovement_Physics_AdjustAirAccelQW(accelqw, (speedlimit - bound(wishspeed, vel_xy_current, speedlimit)) / max(1, speedlimit - wishspeed));
427 vel_xy_forward = vel_xy_current + bound(0, wishspeed - vel_xy_current, step) * accelqw + step * (1 - accelqw);
428 vel_xy_backward = vel_xy_current - bound(0, wishspeed + vel_xy_current, step) * accelqw - step * (1 - accelqw);
429 if(vel_xy_backward < 0)
430 vel_xy_backward = 0; // not that it REALLY occurs that this would cause wrong behaviour afterwards
432 vel_straight = vel_straight + bound(0, wishspeed - vel_straight, step) * accelqw + step * (1 - accelqw);
434 if(sidefric < 0 && VLEN2(vel_perpend))
435 // negative: only apply so much sideways friction to stay below the speed you could get by "braking"
438 f = max(0, 1 + PHYS_INPUT_TIMELENGTH * wishspeed * sidefric);
439 fmin = (vel_xy_backward*vel_xy_backward - vel_straight*vel_straight) / VLEN2(vel_perpend);
441 // vel_xy_backward*vel_xy_backward - vel_straight*vel_straight > vel_perpend*vel_perpend
442 // vel_xy_backward*vel_xy_backward > vel_straight*vel_straight + vel_perpend*vel_perpend
443 // vel_xy_backward*vel_xy_backward > vel_xy * vel_xy
444 // obviously, this cannot be
450 vel_perpend *= max(fmin, f);
454 vel_perpend *= max(0, 1 - PHYS_INPUT_TIMELENGTH * wishspeed * sidefric);
456 s.velocity = vel_perpend + vel_straight * wishdir;
460 float vel_xy_preclamp;
461 vel_xy_preclamp = vlen(s.velocity);
462 if(vel_xy_preclamp > 0) // prevent division by zero
464 vel_xy_current += (vel_xy_forward - vel_xy_current) * speedclamp;
465 if(vel_xy_current < vel_xy_preclamp)
466 s.velocity *= (vel_xy_current / vel_xy_preclamp);
470 s.velocity_z += vel_z;
473 void CSQC_ClientMovement_Physics_PM_AirAccelerate(entity s, vector wishdir, float wishspeed)
475 vector curvel, wishvel, acceldir, curdir;
476 float addspeed, accelspeed, curspeed, f;
484 curspeed = vlen(curvel);
486 if(wishspeed > curspeed * 1.01)
488 wishspeed = min(wishspeed, curspeed + PHYS_WARSOWBUNNY_AIRFORWARDACCEL * PHYS_MAXSPEED * PHYS_INPUT_TIMELENGTH);
492 f = max(0, (PHYS_WARSOWBUNNY_TOPSPEED - curspeed) / (PHYS_WARSOWBUNNY_TOPSPEED - PHYS_MAXSPEED));
493 wishspeed = max(curspeed, PHYS_WARSOWBUNNY_ACCEL) + PHYS_WARSOWBUNNY_ACCEL * f * PHYS_WARSOWBUNNY_ACCEL * PHYS_INPUT_TIMELENGTH;
495 wishvel = wishdir * wishspeed;
496 acceldir = wishvel - curvel;
497 addspeed = vlen(acceldir);
498 acceldir = normalize(acceldir);
500 accelspeed = min(addspeed, PHYS_WARSOWBUNNY_TURNACCEL * PHYS_WARSOWBUNNY_ACCEL * PHYS_INPUT_TIMELENGTH);
502 if(PHYS_WARSOWBUNNY_BACKTOSIDERATIO < 1)
504 curdir = normalize(curvel);
505 dot = acceldir * curdir;
507 acceldir = acceldir - (1 - PHYS_WARSOWBUNNY_BACKTOSIDERATIO) * dot * curdir;
510 s.velocity += accelspeed * acceldir;
513 void CSQC_ClientMovement_Physics_Walk(entity s)
525 // jump if on ground with jump button pressed but only if it has been
526 // released at least once since the last jump
527 if (PHYS_INPUT_BUTTONS(s) & 2)
529 if (IS_ONGROUND(s) && (!IS_JUMP_HELD(s) || !cvar("cl_movement_track_canjump")))
531 s.velocity_z += PHYS_JUMPVELOCITY;
533 SET_JUMP_HELD(s); // canjump = false
537 UNSET_JUMP_HELD(s); // canjump = true
539 // calculate movement vector
541 yawangles_y = PHYS_INPUT_ANGLES(s)_y;
542 makevectors(yawangles);
543 wishvel = PHYS_INPUT_MOVEVALUES(s)_x * v_forward + PHYS_INPUT_MOVEVALUES(s)_y * v_right;
545 // split wishvel into wishspeed and wishdir
546 wishspeed = vlen(wishvel);
548 wishdir = wishvel / wishspeed;
554 wishspeed = min(wishspeed, PHYS_MAXSPEED);
558 // apply edge friction
559 f = sqrt(s.velocity_x * s.velocity_x + s.velocity_y * s.velocity_y);
562 friction = PHYS_FRICTION;
563 if (PHYS_EDGEFRICTION != 1)
567 // note: QW uses the full player box for the trace, and yet still
568 // uses s.origin_z + s.mins_z, which is clearly an bug, but
569 // this mimics it for compatibility
570 neworigin2 = s.origin;
571 neworigin2_x += s.velocity_x*(16/f);
572 neworigin2_y += s.velocity_y*(16/f);
573 neworigin2_z += s.mins_z;
574 neworigin3 = neworigin2;
576 traceline(neworigin2, neworigin3, MOVE_NORMAL, s);
577 if (trace_fraction == 1 && !trace_startsolid)
578 friction *= PHYS_EDGEFRICTION;
580 // apply ground friction
581 f = 1 - PHYS_INPUT_TIMELENGTH * friction * ((f < PHYS_STOPSPEED) ? (PHYS_STOPSPEED / f) : 1);
585 addspeed = wishspeed - dotproduct(s.velocity, wishdir);
588 accelspeed = min(PHYS_ACCELERATE * PHYS_INPUT_TIMELENGTH * wishspeed, addspeed);
589 s.velocity += accelspeed * wishdir;
591 g = PHYS_GRAVITY * PHYS_ENTGRAVITY(s) * PHYS_INPUT_TIMELENGTH;
592 if(!(GAMEPLAYFIX_NOGRAVITYONGROUND))
594 if(GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE)
595 s.velocity_z -= g * 0.5;
599 if (VLEN2(s.velocity))
600 CSQC_ClientMovement_Move(s);
601 if(!(GAMEPLAYFIX_NOGRAVITYONGROUND) || !IS_ONGROUND(s))
603 if(GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE)
604 s.velocity_z -= g * 0.5;
609 if (pmove_waterjumptime <= 0)
611 // apply air speed limit
612 float accel, wishspeed0, wishspeed2, accelqw, strafity;
615 accelqw = PHYS_AIRACCEL_QW;
616 wishspeed0 = wishspeed;
617 wishspeed = min(wishspeed, PHYS_MAXAIRSPEED);
620 accel = PHYS_AIRACCELERATE;
622 accelerating = (dotproduct(s.velocity, wishdir) > 0);
623 wishspeed2 = wishspeed;
626 if(PHYS_AIRSTOPACCELERATE != 0)
629 curdir_x = s.velocity_x;
630 curdir_y = s.velocity_y;
632 curdir = normalize(curdir);
633 accel = accel + (PHYS_AIRSTOPACCELERATE - accel) * max(0, -dotproduct(curdir, wishdir));
635 strafity = IsMoveInDirection(PHYS_INPUT_MOVEVALUES(s), -90) + IsMoveInDirection(PHYS_INPUT_MOVEVALUES(s), +90); // if one is nonzero, other is always zero
636 if(PHYS_MAXAIRSTRAFESPEED)
637 wishspeed = min(wishspeed, GeomLerp(PHYS_MAXAIRSPEED, strafity, PHYS_MAXAIRSTRAFESPEED));
638 if(PHYS_AIRSTRAFEACCELERATE)
639 accel = GeomLerp(PHYS_AIRACCELERATE, strafity, PHYS_AIRSTRAFEACCELERATE);
640 if(PHYS_AIRSTRAFEACCEL_QW)
642 (((strafity > 0.5 ? PHYS_AIRSTRAFEACCEL_QW : PHYS_AIRACCEL_QW) >= 0) ? +1 : -1)
644 (1 - GeomLerp(1 - fabs(PHYS_AIRACCEL_QW), strafity, 1 - fabs(PHYS_AIRSTRAFEACCEL_QW)));
647 if(PHYS_WARSOWBUNNY_TURNACCEL && accelerating && PHYS_INPUT_MOVEVALUES(s)_y == 0 && PHYS_INPUT_MOVEVALUES(s)_x != 0)
648 CSQC_ClientMovement_Physics_PM_AirAccelerate(s, wishdir, wishspeed2);
650 CSQC_ClientMovement_Physics_PM_Accelerate(s, wishdir, wishspeed, wishspeed0, accel, accelqw, PHYS_AIRACCEL_QW_STRETCHFACTOR, PHYS_AIRACCEL_SIDEWAYS_FRICTION / PHYS_MAXAIRSPEED, PHYS_AIRSPEEDLIMIT_NONQW);
653 CSQC_ClientMovement_Physics_CPM_PM_Aircontrol(s, wishdir, wishspeed2);
655 g = PHYS_GRAVITY * PHYS_ENTGRAVITY(s) * PHYS_INPUT_TIMELENGTH;
656 if(GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE)
657 s.velocity_z -= g * 0.5;
660 CSQC_ClientMovement_Move(s);
661 if(!(GAMEPLAYFIX_NOGRAVITYONGROUND) || !IS_ONGROUND(s))
663 if(GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE)
664 s.velocity_z -= g * 0.5;
669 // TODO: merge this with main physics frame
670 void CSQC_ClientMovement_Physics_Swim(entity s)
673 self.flags &= ~FL_ONGROUND;
675 makevectors(PHYS_INPUT_ANGLES(s));
676 //wishvel = v_forward * self.movement_x + v_right * self.movement_y + v_up * self.movement_z;
677 vector wishvel = v_forward * PHYS_INPUT_MOVEVALUES(s)_x + v_right * PHYS_INPUT_MOVEVALUES(s)_y + '0 0 1' * PHYS_INPUT_MOVEVALUES(s)_z;
678 if (wishvel == '0 0 0')
679 wishvel = '0 0 -60'; // drift towards bottom
681 vector wishdir = normalize(wishvel);
682 float wishspeed = vlen(wishvel);
683 if (wishspeed > PHYS_MAXSPEED)
684 wishspeed = PHYS_MAXSPEED;
685 wishspeed = wishspeed * 0.7;
688 self.velocity = self.velocity * (1 - PHYS_INPUT_TIMELENGTH * PHYS_FRICTION);
690 // water acceleration
691 CSQC_ClientMovement_Physics_PM_Accelerate(s, wishdir, wishspeed, wishspeed, PHYS_ACCELERATE, 1, 0, 0, 0);
694 void CSQC_ClientMovement_PlayerMove(entity s)
696 //Con_Printf(" %f", frametime);
697 if (!(PHYS_INPUT_BUTTONS(s) & 2)) // !jump
698 UNSET_JUMP_HELD(s); // canjump = true
699 pmove_waterjumptime -= PHYS_INPUT_TIMELENGTH;
700 CSQC_ClientMovement_UpdateStatus(s);
701 if (s.waterlevel >= WATERLEVEL_SWIMMING)
702 CSQC_ClientMovement_Physics_Swim(s);
704 CSQC_ClientMovement_Physics_Walk(s);
707 void CSQC_ClientMovement_PlayerMove_Frame(entity s)
709 // if a move is more than 50ms, do it as two moves (matching qwsv)
710 //Con_Printf("%i ", s.cmd.msec);
711 if(PHYS_INPUT_TIMELENGTH > 0.0005)
713 if (PHYS_INPUT_TIMELENGTH > 0.05)
715 PHYS_INPUT_TIMELENGTH /= 2;
716 CSQC_ClientMovement_PlayerMove(s);
718 CSQC_ClientMovement_PlayerMove(s);
722 // we REALLY need this handling to happen, even if the move is not executed
723 if (!(PHYS_INPUT_BUTTONS(s) & 2)) // !jump
724 UNSET_JUMP_HELD(s); // canjump = true
730 #undef PHYS_INPUT_ANGLES
731 #undef PHYS_INPUT_BUTTONS
733 #undef PHYS_INPUT_TIMELENGTH
735 #undef PHYS_INPUT_MOVEVALUES
737 #undef GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE
738 #undef GAMEPLAYFIX_NOGRAVITYONGROUND
739 #undef GAMEPLAYFIX_Q2AIRACCELERATE
747 #undef UNSET_JUMP_HELD
751 #undef UNSET_ONGROUND
753 #undef PHYS_ACCELERATE
754 #undef PHYS_AIRACCEL_QW
755 #undef PHYS_AIRACCEL_QW_STRETCHFACTOR
756 #undef PHYS_AIRACCEL_SIDEWAYS_FRICTION
757 #undef PHYS_AIRACCELERATE
758 #undef PHYS_AIRCONTROL
759 #undef PHYS_AIRCONTROL_PENALTY
760 #undef PHYS_AIRCONTROL_POWER
761 #undef PHYS_AIRSPEEDLIMIT_NONQW
762 #undef PHYS_AIRSTOPACCELERATE
763 #undef PHYS_AIRSTRAFEACCEL_QW
764 #undef PHYS_AIRSTRAFEACCELERATE
765 #undef PHYS_EDGEFRICTION
766 #undef PHYS_ENTGRAVITY
769 #undef PHYS_JUMPVELOCITY
770 #undef PHYS_MAXAIRSPEED
771 #undef PHYS_MAXAIRSTRAFESPEED
773 #undef PHYS_STEPHEIGHT
774 #undef PHYS_STOPSPEED
775 #undef PHYS_WARSOWBUNNY_ACCEL
776 #undef PHYS_WARSOWBUNNY_BACKTOSIDERATIO
777 #undef PHYS_WARSOWBUNNY_AIRFORWARDACCEL
778 #undef PHYS_WARSOWBUNNY_TOPSPEED
779 #undef PHYS_WARSOWBUNNY_TURNACCEL