From: TimePath Date: Mon, 8 Dec 2014 09:35:52 +0000 (+1100) Subject: Merge client and server physics routines X-Git-Tag: xonotic-v0.8.1~38^2~109 X-Git-Url: https://git.rm.cloudns.org/?a=commitdiff_plain;h=3ef2bf8bf399e23265b0eb853886cfec1659f332;p=xonotic%2Fxonotic-data.pk3dir.git Merge client and server physics routines --- diff --git a/qcsrc/common/physics.qc b/qcsrc/common/physics.qc index 2b4fa4ecf..17f3289e6 100644 --- a/qcsrc/common/physics.qc +++ b/qcsrc/common/physics.qc @@ -1,4 +1,3 @@ -#ifdef SVQC .float race_penalty; .float restart_jump; @@ -17,12 +16,9 @@ .string lastclassname; .float() PlayerPhysplug; -#endif - -// TODO: water prediction // TODO: move to a common header -#define VLEN2(v) dotproduct(v, v) +#define VLEN2(v) v * v // Client/server mappings #ifdef CSQC @@ -51,21 +47,21 @@ #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_QW(s) getstatf(STAT_MOVEVARS_AIRACCEL_QW) + #define PHYS_AIRACCEL_QW_STRETCHFACTOR(s) 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_AIRSPEEDLIMIT_NONQW(s) getstatf(STAT_MOVEVARS_AIRSPEEDLIMIT_NONQW) #define PHYS_AIRSTOPACCELERATE getstatf(STAT_MOVEVARS_AIRSTOPACCELERATE) - #define PHYS_AIRSTRAFEACCEL_QW getstatf(STAT_MOVEVARS_AIRSTRAFEACCEL_QW) + #define PHYS_AIRSTRAFEACCEL_QW(s) 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_HIGHSPEED getstatf(STAT_MOVEVARS_HIGHSPEED) #define PHYS_JUMPVELOCITY getstatf(STAT_MOVEVARS_JUMPVELOCITY) #define PHYS_MAXAIRSPEED getstatf(STAT_MOVEVARS_MAXAIRSPEED) #define PHYS_MAXAIRSTRAFESPEED getstatf(STAT_MOVEVARS_MAXAIRSTRAFESPEED) @@ -81,8 +77,8 @@ #elif defined(SVQC) #define PHYS_INPUT_ANGLES(s) s.v_angle - // FIXME - #define PHYS_INPUT_BUTTONS(s) 0 + // TODO: cache + #define PHYS_INPUT_BUTTONS(s) (s.BUTTON_ATCK + 2 * s.BUTTON_JUMP + 4 * s.BUTTON_ATCK2 + 8 * s.BUTTON_ZOOM + 16 * s.BUTTON_CROUCH + 32 * s.BUTTON_HOOK + 64 * s.BUTTON_USE + 128 * (s.movement_x < 0) + 256 * (s.movement_x > 0) + 512 * (s.movement_y < 0) + 1024 * (s.movement_y > 0)) #define PHYS_INPUT_TIMELENGTH frametime @@ -105,21 +101,21 @@ #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_QW(s) s.stat_sv_airaccel_qw + #define PHYS_AIRACCEL_QW_STRETCHFACTOR(s) 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_AIRSPEEDLIMIT_NONQW(s) s.stat_sv_airspeedlimit_nonqw #define PHYS_AIRSTOPACCELERATE autocvar_sv_airstopaccelerate - #define PHYS_AIRSTRAFEACCEL_QW autocvar_sv_airstrafeaccel_qw + #define PHYS_AIRSTRAFEACCEL_QW(s) s.stat_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_HIGHSPEED autocvar_g_movement_highspeed #define PHYS_JUMPVELOCITY autocvar_sv_jumpvelocity #define PHYS_MAXAIRSPEED autocvar_sv_maxairspeed #define PHYS_MAXAIRSTRAFESPEED autocvar_sv_maxairstrafespeed @@ -773,35 +769,35 @@ void RaceCarPhysics() { if(myspeed > 0) { - myspeed = max(0, myspeed - frametime * (g_bugrigs_friction_floor - g_bugrigs_friction_brake * accel)); + myspeed = max(0, myspeed - PHYS_INPUT_TIMELENGTH * (g_bugrigs_friction_floor - g_bugrigs_friction_brake * accel)); } else { if(!g_bugrigs_reverse_speeding) - myspeed = min(0, myspeed + frametime * g_bugrigs_friction_floor); + myspeed = min(0, myspeed + PHYS_INPUT_TIMELENGTH * g_bugrigs_friction_floor); } } else { if(myspeed >= 0) { - myspeed = max(0, myspeed - frametime * g_bugrigs_friction_floor); + myspeed = max(0, myspeed - PHYS_INPUT_TIMELENGTH * g_bugrigs_friction_floor); } else { if(g_bugrigs_reverse_stopping) myspeed = 0; else - myspeed = min(0, myspeed + frametime * (g_bugrigs_friction_floor + g_bugrigs_friction_brake * accel)); + myspeed = min(0, myspeed + PHYS_INPUT_TIMELENGTH * (g_bugrigs_friction_floor + g_bugrigs_friction_brake * accel)); } } // terminal velocity = velocity at which 50 == accelfactor, that is, 1549 units/sec //MAXIMA: friction(v) := g_bugrigs_friction_floor; - self.angles_y += steer * frametime * steerfactor; // apply steering + self.angles_y += steer * PHYS_INPUT_TIMELENGTH * steerfactor; // apply steering makevectors(self.angles); // new forward direction! - myspeed += accel * accelfactor * frametime; + myspeed += accel * accelfactor * PHYS_INPUT_TIMELENGTH; rigvel = myspeed * v_forward + '0 0 1' * upspeed; } @@ -812,13 +808,13 @@ void RaceCarPhysics() // responsiveness factor for steering and acceleration f = 1 / (1 + pow(max(0, myspeed / g_bugrigs_speed_ref), g_bugrigs_speed_pow)); steerfactor = -myspeed * f; - self.angles_y += steer * frametime * steerfactor; // apply steering + self.angles_y += steer * PHYS_INPUT_TIMELENGTH * steerfactor; // apply steering rigvel = self.velocity; makevectors(self.angles); // new forward direction! } - rigvel = rigvel * max(0, 1 - vlen(rigvel) * g_bugrigs_friction_air * frametime); + rigvel = rigvel * max(0, 1 - vlen(rigvel) * g_bugrigs_friction_air * PHYS_INPUT_TIMELENGTH); //MAXIMA: airfriction(v) := v * v * g_bugrigs_friction_air; //MAXIMA: total_acceleration(v) := accel(v) - friction(v) - airfriction(v); //MAXIMA: solve(total_acceleration(v) = 0, v); @@ -828,7 +824,7 @@ void RaceCarPhysics() vector rigvel_xy, neworigin, up; float mt; - rigvel_z -= frametime * autocvar_sv_gravity; // 4x gravity plays better + rigvel_z -= PHYS_INPUT_TIMELENGTH * autocvar_sv_gravity; // 4x gravity plays better rigvel_xy = vec2(rigvel); if(g_bugrigs_planar_movement_car_jumping) @@ -841,10 +837,10 @@ void RaceCarPhysics() // BUG RIGS: align the move to the surface instead of doing collision testing // can we move? - tracebox(trace_endpos, self.mins, self.maxs, trace_endpos + rigvel_xy * frametime, mt, self); + tracebox(trace_endpos, self.mins, self.maxs, trace_endpos + rigvel_xy * PHYS_INPUT_TIMELENGTH, mt, self); // align to surface - tracebox(trace_endpos, self.mins, self.maxs, trace_endpos - up + '0 0 1' * rigvel_z * frametime, mt, self); + tracebox(trace_endpos, self.mins, self.maxs, trace_endpos - up + '0 0 1' * rigvel_z * PHYS_INPUT_TIMELENGTH, mt, self); if(trace_fraction < 0.5) { @@ -872,12 +868,12 @@ void RaceCarPhysics() self.flags &= ~FL_ONGROUND; } - self.velocity = (neworigin - self.origin) * (1.0 / frametime); + self.velocity = (neworigin - self.origin) * (1.0 / PHYS_INPUT_TIMELENGTH); self.movetype = MOVETYPE_NOCLIP; } else { - rigvel_z -= frametime * autocvar_sv_gravity; // 4x gravity plays better + rigvel_z -= PHYS_INPUT_TIMELENGTH * autocvar_sv_gravity; // 4x gravity plays better self.velocity = rigvel; self.movetype = MOVETYPE_FLY; } @@ -910,7 +906,7 @@ void RaceCarPhysics() // smooth the angles vector vf1, vu1, smoothangles; makevectors(self.angles); - f = bound(0, frametime * g_bugrigs_angle_smoothing, 1); + f = bound(0, PHYS_INPUT_TIMELENGTH * g_bugrigs_angle_smoothing, 1); if(f == 0) f = 1; vf1 = v_forward * f; @@ -977,14 +973,14 @@ float PM_check_keepaway(void) #ifdef SVQC return (self.ballcarried && g_keepaway) ? autocvar_g_keepaway_ballcarrier_highspeed : 1; #else - return 0; + return 1; #endif } void PM_check_race_movetime(void) { #ifdef SVQC - self.race_movetime_frac += frametime; + self.race_movetime_frac += PHYS_INPUT_TIMELENGTH; float f = floor(self.race_movetime_frac); self.race_movetime_frac -= f; self.race_movetime_count += f; @@ -1057,7 +1053,7 @@ void PM_check_punch() float f; if (self.punchangle != '0 0 0') { - f = vlen(self.punchangle) - 10 * frametime; + f = vlen(self.punchangle) - 10 * PHYS_INPUT_TIMELENGTH; if (f > 0) self.punchangle = normalize(self.punchangle) * f; else @@ -1066,7 +1062,7 @@ void PM_check_punch() if (self.punchvector != '0 0 0') { - f = vlen(self.punchvector) - 30 * frametime; + f = vlen(self.punchvector) - 30 * PHYS_INPUT_TIMELENGTH; if (f > 0) self.punchvector = normalize(self.punchvector) * f; else @@ -1169,55 +1165,55 @@ void PM_check_vortex(void) xyspeed = min(xyspeed, autocvar_g_balance_nex_charge_maxspeed); float f = (xyspeed - autocvar_g_balance_nex_charge_minspeed) / (autocvar_g_balance_nex_charge_maxspeed - autocvar_g_balance_nex_charge_minspeed); // add the extra charge - self.nex_charge = min(1, self.nex_charge + autocvar_g_balance_nex_charge_velocity_rate * f * frametime); + self.nex_charge = min(1, self.nex_charge + autocvar_g_balance_nex_charge_velocity_rate * f * PHYS_INPUT_TIMELENGTH); } #endif } void PM_fly(float maxspd_mod) { -#ifdef SVQC // noclipping or flying self.flags &= ~FL_ONGROUND; - self.velocity = self.velocity * (1 - frametime * autocvar_sv_friction); + self.velocity = self.velocity * (1 - PHYS_INPUT_TIMELENGTH * PHYS_FRICTION); makevectors(self.v_angle); //wishvel = v_forward * self.movement_x + v_right * self.movement_y + v_up * self.movement_z; - vector wishvel = v_forward * self.movement_x + v_right * self.movement_y + '0 0 1' * self.movement_z; + vector wishvel = v_forward * PHYS_INPUT_MOVEVALUES(self).x + + v_right * PHYS_INPUT_MOVEVALUES(self).y + + '0 0 1' * PHYS_INPUT_MOVEVALUES(self).z; // acceleration vector wishdir = normalize(wishvel); float wishspeed = vlen(wishvel); - if (wishspeed > self.stat_sv_maxspeed*maxspd_mod) - wishspeed = self.stat_sv_maxspeed*maxspd_mod; + if (wishspeed > PHYS_MAXSPEED(self) * maxspd_mod) + wishspeed = PHYS_MAXSPEED(self) * maxspd_mod; if (time >= self.teleport_time) - PM_Accelerate(self, wishdir, wishspeed, wishspeed, autocvar_sv_accelerate*maxspd_mod, 1, 0, 0, 0); -#endif + PM_Accelerate(self, wishdir, wishspeed, wishspeed, PHYS_ACCELERATE * maxspd_mod, 1, 0, 0, 0); } void PM_swim(float maxspd_mod) { -#ifdef SVQC // swimming self.flags &= ~FL_ONGROUND; makevectors(self.v_angle); //wishvel = v_forward * self.movement_x + v_right * self.movement_y + v_up * self.movement_z; - vector wishvel = v_forward * self.movement_x + v_right * self.movement_y + '0 0 1' * self.movement_z; + vector wishvel = v_forward * PHYS_INPUT_MOVEVALUES(self).x + + v_right * PHYS_INPUT_MOVEVALUES(self).y + + '0 0 1' * PHYS_INPUT_MOVEVALUES(self).z; if (wishvel == '0 0 0') wishvel = '0 0 -60'; // drift towards bottom vector wishdir = normalize(wishvel); float wishspeed = vlen(wishvel); - if (wishspeed > self.stat_sv_maxspeed*maxspd_mod) - wishspeed = self.stat_sv_maxspeed*maxspd_mod; + if (wishspeed > PHYS_MAXSPEED(self) * maxspd_mod) + wishspeed = PHYS_MAXSPEED(self) * maxspd_mod; wishspeed = wishspeed * 0.7; // water friction - self.velocity = self.velocity * (1 - frametime * autocvar_sv_friction); + self.velocity = self.velocity * (1 - PHYS_INPUT_TIMELENGTH * PHYS_FRICTION); // water acceleration - PM_Accelerate(self, wishdir, wishspeed, wishspeed, autocvar_sv_accelerate*maxspd_mod, 1, 0, 0, 0); -#endif + PM_Accelerate(self, wishdir, wishspeed, wishspeed, PHYS_ACCELERATE*maxspd_mod, 1, 0, 0, 0); } void PM_ladder(float maxspd_mod) @@ -1227,7 +1223,7 @@ void PM_ladder(float maxspd_mod) self.flags &= ~FL_ONGROUND; float g; - g = autocvar_sv_gravity * frametime; + g = autocvar_sv_gravity * PHYS_INPUT_TIMELENGTH; if(self.gravity) g *= self.gravity; if(autocvar_sv_gameplayfix_gravityunaffectedbyticrate) @@ -1236,7 +1232,7 @@ void PM_ladder(float maxspd_mod) self.velocity_z += g; } - self.velocity = self.velocity * (1 - frametime * autocvar_sv_friction); + self.velocity = self.velocity * (1 - PHYS_INPUT_TIMELENGTH * PHYS_FRICTION); makevectors(self.v_angle); //wishvel = v_forward * self.movement_x + v_right * self.movement_y + v_up * self.movement_z; vector wishvel = v_forward * self.movement_x + v_right * self.movement_y + '0 0 1' * self.movement_z; @@ -1269,7 +1265,7 @@ void PM_ladder(float maxspd_mod) if (time >= self.teleport_time) { // water acceleration - PM_Accelerate(self, wishdir, wishspeed, wishspeed, autocvar_sv_accelerate*maxspd_mod, 1, 0, 0, 0); + PM_Accelerate(self, wishdir, wishspeed, wishspeed, PHYS_ACCELERATE*maxspd_mod, 1, 0, 0, 0); } #endif } @@ -1281,7 +1277,7 @@ void PM_jetpack(float maxspd_mod) makevectors(self.v_angle); vector wishvel = v_forward * self.movement_x + v_right * self.movement_y; // add remaining speed as Z component - float maxairspd = autocvar_sv_maxairspeed*max(1, maxspd_mod); + float maxairspd = PHYS_MAXAIRSPEED*max(1, maxspd_mod); // fix speedhacks :P wishvel = normalize(wishvel) * min(vlen(wishvel) / maxairspd, 1); // add the unused velocity as up component @@ -1357,7 +1353,7 @@ void PM_jetpack(float maxspd_mod) fvel = min(1, vlen(wishvel) / best); if(autocvar_g_jetpack_fuel && !(self.items & IT_UNLIMITED_WEAPON_AMMO)) - f = min(1, self.ammo_fuel / (autocvar_g_jetpack_fuel * frametime * fvel)); + f = min(1, self.ammo_fuel / (autocvar_g_jetpack_fuel * PHYS_INPUT_TIMELENGTH * fvel)); else f = 1; @@ -1365,9 +1361,9 @@ void PM_jetpack(float maxspd_mod) if (f > 0 && wishvel != '0 0 0') { - self.velocity = self.velocity + wishvel * f * frametime; + self.velocity = self.velocity + wishvel * f * PHYS_INPUT_TIMELENGTH; if (!(self.items & IT_UNLIMITED_WEAPON_AMMO)) - self.ammo_fuel -= autocvar_g_jetpack_fuel * frametime * fvel * f; + self.ammo_fuel -= autocvar_g_jetpack_fuel * PHYS_INPUT_TIMELENGTH * fvel * f; self.flags &= ~FL_ONGROUND; self.items |= IT_USING_JETPACK; @@ -1383,34 +1379,23 @@ void PM_walk(float buttons_prev, float maxspd_mod) // we get here if we ran out of ammo if((self.items & IT_JETPACK) && self.BUTTON_HOOK && !(buttons_prev & 32) && self.ammo_fuel < 0.01) sprint(self, "You don't have any fuel for the ^2Jetpack\n"); - +#endif // walking - makevectors(self.v_angle_y * '0 1 0'); - vector wishvel = v_forward * self.movement_x + v_right * self.movement_y; + makevectors(PHYS_INPUT_ANGLES(self).y * '0 1 0'); + vector wishvel = v_forward * PHYS_INPUT_MOVEVALUES(self).x + + v_right * PHYS_INPUT_MOVEVALUES(self).y; +#ifdef SVQC if(!(self.lastflags & FL_ONGROUND)) { if(autocvar_speedmeter) dprint(strcat("landing velocity: ", vtos(self.velocity), " (abs: ", ftos(vlen(self.velocity)), ")\n")); if(self.lastground < time - 0.3) - self.velocity = self.velocity * (1 - autocvar_sv_friction_on_land); + self.velocity *= (1 - autocvar_sv_friction_on_land); if(self.jumppadcount > 1) dprint(strcat(ftos(self.jumppadcount), "x jumppad combo\n")); self.jumppadcount = 0; } - -#ifdef LETS_TEST_FTEQCC - if(self.velocity_x || self.velocity_y) - { - // good - } - else - { - if(self.velocity_x) - checkclient(); - if(self.velocity_y) - checkclient(); - } #endif vector v = self.velocity; @@ -1418,333 +1403,191 @@ void PM_walk(float buttons_prev, float maxspd_mod) float f = vlen(v); if(f > 0) { - if (f < autocvar_sv_stopspeed) - f = 1 - frametime * (autocvar_sv_stopspeed / f) * autocvar_sv_friction; - else - f = 1 - frametime * autocvar_sv_friction; - if (f > 0) - self.velocity = self.velocity * f; - else - self.velocity = '0 0 0'; + f = 1 - PHYS_INPUT_TIMELENGTH * PHYS_FRICTION * ((f < PHYS_STOPSPEED) ? (PHYS_STOPSPEED / f) : 1); + f = max(f, 0); + self.velocity *= f; /* Mathematical analysis time! Our goal is to invert this mess. For the two cases we get: - v = v0 * (1 - frametime * (autocvar_sv_stopspeed / v0) * autocvar_sv_friction) - = v0 - frametime * autocvar_sv_stopspeed * autocvar_sv_friction - v0 = v + frametime * autocvar_sv_stopspeed * autocvar_sv_friction + v = v0 * (1 - PHYS_INPUT_TIMELENGTH * (PHYS_STOPSPEED / v0) * PHYS_FRICTION) + = v0 - PHYS_INPUT_TIMELENGTH * PHYS_STOPSPEED * PHYS_FRICTION + v0 = v + PHYS_INPUT_TIMELENGTH * PHYS_STOPSPEED * PHYS_FRICTION and - v = v0 * (1 - frametime * autocvar_sv_friction) - v0 = v / (1 - frametime * autocvar_sv_friction) + v = v0 * (1 - PHYS_INPUT_TIMELENGTH * PHYS_FRICTION) + v0 = v / (1 - PHYS_INPUT_TIMELENGTH * PHYS_FRICTION) These cases would be chosen ONLY if: - v0 < autocvar_sv_stopspeed - v + frametime * autocvar_sv_stopspeed * autocvar_sv_friction < autocvar_sv_stopspeed - v < autocvar_sv_stopspeed * (1 - frametime * autocvar_sv_friction) + v0 < PHYS_STOPSPEED + v + PHYS_INPUT_TIMELENGTH * PHYS_STOPSPEED * PHYS_FRICTION < PHYS_STOPSPEED + v < PHYS_STOPSPEED * (1 - PHYS_INPUT_TIMELENGTH * PHYS_FRICTION) and, respectively: - v0 >= autocvar_sv_stopspeed - v / (1 - frametime * autocvar_sv_friction) >= autocvar_sv_stopspeed - v >= autocvar_sv_stopspeed * (1 - frametime * autocvar_sv_friction) + v0 >= PHYS_STOPSPEED + v / (1 - PHYS_INPUT_TIMELENGTH * PHYS_FRICTION) >= PHYS_STOPSPEED + v >= PHYS_STOPSPEED * (1 - PHYS_INPUT_TIMELENGTH * PHYS_FRICTION) */ } // acceleration vector wishdir = normalize(wishvel); float wishspeed = vlen(wishvel); - if (wishspeed > self.stat_sv_maxspeed*maxspd_mod) - wishspeed = self.stat_sv_maxspeed*maxspd_mod; - if (self.crouch) - wishspeed = wishspeed * 0.5; + wishspeed = min(wishspeed, PHYS_MAXSPEED(self) * maxspd_mod); + if (IS_DUCKED(self)) + wishspeed *= 0.5; +#ifdef CSQC + float addspeed = wishspeed - dotproduct(self.velocity, wishdir); + if (addspeed > 0) + { + float accelspeed = min(PHYS_ACCELERATE * PHYS_INPUT_TIMELENGTH * wishspeed, addspeed); + self.velocity += accelspeed * wishdir; + } + float g = PHYS_GRAVITY * PHYS_ENTGRAVITY(self) * PHYS_INPUT_TIMELENGTH; + if(!(GAMEPLAYFIX_NOGRAVITYONGROUND)) + { + if(GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE) + self.velocity_z -= g * 0.5; + else + self.velocity_z -= g; + } + if (VLEN2(self.velocity)) + CSQC_ClientMovement_Move(self); + if(!(GAMEPLAYFIX_NOGRAVITYONGROUND) || !IS_ONGROUND(self)) + { + if(GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE) + self.velocity_z -= g * 0.5; + } +#else if (time >= self.teleport_time) - PM_Accelerate(self, wishdir, wishspeed, wishspeed, autocvar_sv_accelerate*maxspd_mod, 1, 0, 0, 0); + PM_Accelerate(self, wishdir, wishspeed, wishspeed, PHYS_ACCELERATE * maxspd_mod, 1, 0, 0, 0); #endif } void PM_air(float buttons_prev, float maxspd_mod) { -#ifdef SVQC float wishspeed0; +#ifdef SVQC // we get here if we ran out of ammo if((self.items & IT_JETPACK) && self.BUTTON_HOOK && !(buttons_prev & 32) && self.ammo_fuel < 0.01) sprint(self, "You don't have any fuel for the ^2Jetpack\n"); - +#endif float maxairspd, airaccel; - if(maxspd_mod < 1) - { - maxairspd = autocvar_sv_maxairspeed*maxspd_mod; - airaccel = autocvar_sv_airaccelerate*maxspd_mod; - } - else - { - maxairspd = autocvar_sv_maxairspeed; - airaccel = autocvar_sv_airaccelerate; - } + maxairspd = PHYS_MAXAIRSPEED * min(maxspd_mod, 1); + airaccel = PHYS_AIRACCELERATE * min(maxspd_mod, 1); // airborn - makevectors(self.v_angle_y * '0 1 0'); - vector wishvel = v_forward * self.movement_x + v_right * self.movement_y; + makevectors(PHYS_INPUT_ANGLES(self).y * '0 1 0'); + vector wishvel = v_forward * PHYS_INPUT_MOVEVALUES(self).x + + v_right * PHYS_INPUT_MOVEVALUES(self).y; // acceleration vector wishdir = normalize(wishvel); float wishspeed = wishspeed0 = vlen(wishvel); - if (wishspeed0 > self.stat_sv_maxspeed*maxspd_mod) - wishspeed0 = self.stat_sv_maxspeed*maxspd_mod; - if (wishspeed > maxairspd) - wishspeed = maxairspd; - if (self.crouch) - wishspeed = wishspeed * 0.5; + wishspeed0 = min(wishspeed0, PHYS_MAXSPEED(self) * maxspd_mod); + wishspeed = min(wishspeed, maxairspd); + if (IS_DUCKED(self)) + wishspeed *= 0.5; +#ifdef SVQC if (time >= self.teleport_time) +#else + if (pmove_waterjumptime <= 0) +#endif { float accelerating; float wishspeed2; float airaccelqw; float strafity; - airaccelqw = self.stat_sv_airaccel_qw; + airaccelqw = PHYS_AIRACCEL_QW(self); accelerating = (self.velocity * wishdir > 0); wishspeed2 = wishspeed; // CPM - if(autocvar_sv_airstopaccelerate) + if(PHYS_AIRSTOPACCELERATE) { vector curdir; curdir = self.velocity; curdir_z = 0; curdir = normalize(curdir); - airaccel = airaccel + (autocvar_sv_airstopaccelerate*maxspd_mod - airaccel) * max(0, -(curdir * wishdir)); + airaccel = airaccel + (PHYS_AIRSTOPACCELERATE*maxspd_mod - airaccel) * max(0, -(curdir * wishdir)); } // note that for straight forward jumping: - // step = accel * frametime * wishspeed0; + // step = accel * PHYS_INPUT_TIMELENGTH * wishspeed0; // accel = bound(0, wishspeed - vel_xy_current, step) * accelqw + step * (1 - accelqw); // --> // dv/dt = accel * maxspeed (when slow) // dv/dt = accel * maxspeed * (1 - accelqw) (when fast) // log dv/dt = logaccel + logmaxspeed (when slow) // log dv/dt = logaccel + logmaxspeed + log(1 - accelqw) (when fast) - strafity = IsMoveInDirection(self.movement, -90) + IsMoveInDirection(self.movement, +90); // if one is nonzero, other is always zero - if(autocvar_sv_maxairstrafespeed) - wishspeed = min(wishspeed, GeomLerp(autocvar_sv_maxairspeed*maxspd_mod, strafity, autocvar_sv_maxairstrafespeed*maxspd_mod)); - if(autocvar_sv_airstrafeaccelerate) - airaccel = GeomLerp(airaccel, strafity, autocvar_sv_airstrafeaccelerate*maxspd_mod); - if(self.stat_sv_airstrafeaccel_qw) - airaccelqw = copysign(1-GeomLerp(1-fabs(self.stat_sv_airaccel_qw), strafity, 1-fabs(self.stat_sv_airstrafeaccel_qw)), ((strafity > 0.5) ? self.stat_sv_airstrafeaccel_qw : self.stat_sv_airaccel_qw)); + strafity = IsMoveInDirection(PHYS_INPUT_MOVEVALUES(self), -90) + IsMoveInDirection(PHYS_INPUT_MOVEVALUES(self), +90); // if one is nonzero, other is always zero + if(PHYS_MAXAIRSTRAFESPEED) + wishspeed = min(wishspeed, GeomLerp(PHYS_MAXAIRSPEED*maxspd_mod, strafity, PHYS_MAXAIRSTRAFESPEED*maxspd_mod)); + if(PHYS_AIRSTRAFEACCELERATE) + airaccel = GeomLerp(airaccel, strafity, PHYS_AIRSTRAFEACCELERATE*maxspd_mod); + if(PHYS_AIRSTRAFEACCEL_QW(self)) + airaccelqw = copysign(1-GeomLerp(1-fabs(PHYS_AIRACCEL_QW(self)), strafity, 1-fabs(PHYS_AIRSTRAFEACCEL_QW(self))), ((strafity > 0.5) ? PHYS_AIRSTRAFEACCEL_QW(self) : PHYS_AIRACCEL_QW(self))); // !CPM - if(autocvar_sv_warsowbunny_turnaccel && accelerating && self.movement_y == 0 && self.movement_x != 0) + if(PHYS_WARSOWBUNNY_TURNACCEL && accelerating && PHYS_INPUT_MOVEVALUES(self).y == 0 && PHYS_INPUT_MOVEVALUES(self).x != 0) PM_AirAccelerate(self, wishdir, wishspeed); else - PM_Accelerate(self, wishdir, wishspeed, wishspeed0, airaccel, airaccelqw, autocvar_sv_airaccel_qw_stretchfactor, autocvar_sv_airaccel_sideways_friction / maxairspd, self.stat_sv_airspeedlimit_nonqw); + PM_Accelerate(self, wishdir, wishspeed, wishspeed0, airaccel, airaccelqw, PHYS_AIRACCEL_QW_STRETCHFACTOR(self), PHYS_AIRACCEL_SIDEWAYS_FRICTION / maxairspd, PHYS_AIRSPEEDLIMIT_NONQW(self)); - if(autocvar_sv_aircontrol) + if(PHYS_AIRCONTROL) CPM_PM_Aircontrol(self, wishdir, wishspeed2); } -#endif -} - -// TODO: merge this with main physics frame -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(s)); - 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; - } - } +#ifdef CSQC + float g = PHYS_GRAVITY * PHYS_ENTGRAVITY(s) * PHYS_INPUT_TIMELENGTH; + if(GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE) + self.velocity_z -= g * 0.5; else + self.velocity_z -= g; + CSQC_ClientMovement_Move(self); + if(!(GAMEPLAYFIX_NOGRAVITYONGROUND) || !IS_ONGROUND(self)) { - 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) - PM_AirAccelerate(s, wishdir, wishspeed2); - else - PM_Accelerate(s, wishdir, wishspeed, wishspeed0, accel, accelqw, PHYS_AIRACCEL_QW_STRETCHFACTOR, PHYS_AIRACCEL_SIDEWAYS_FRICTION / PHYS_MAXAIRSPEED, PHYS_AIRSPEEDLIMIT_NONQW); - - if(PHYS_AIRCONTROL) - 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; - } + self.velocity_z -= g * 0.5; } +#endif } -// TODO: merge this with main physics frame -void CSQC_ClientMovement_Physics_Swim(entity s) +#ifdef CSQC +// Copied from server/g_damage.qc, why is it even in there? +float IsFlying(entity a) { - // 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(s)) - wishspeed = PHYS_MAXSPEED(s); - wishspeed = wishspeed * 0.7; - - // water friction - self.velocity = self.velocity * (1 - PHYS_INPUT_TIMELENGTH * PHYS_FRICTION); - - // water acceleration - PM_Accelerate(s, wishdir, wishspeed, wishspeed, PHYS_ACCELERATE, 1, 0, 0, 0); + if(a.flags & FL_ONGROUND) + return 0; + if(a.waterlevel >= WATERLEVEL_SWIMMING) + return 0; + traceline(a.origin, a.origin - '0 0 48', MOVE_NORMAL, a); + if(trace_fraction < 1) + return 0; + return 1; } +#endif void PM_Main(entity s) { #ifdef CSQC - //Con_Printf(" %f", frametime); + //Con_Printf(" %f", PHYS_INPUT_TIMELENGTH); if (!(PHYS_INPUT_BUTTONS(s) & 2)) // !jump UNSET_JUMP_HELD(s); // canjump = true pmove_waterjumptime -= PHYS_INPUT_TIMELENGTH; CSQC_ClientMovement_UpdateStatus(s); #endif -#ifdef SVQC + float maxspd_mod, buttons; float buttons_prev; float not_allowed_to_move; - +#ifdef SVQC WarpZone_PlayerPhysics_FixVAngle(); - +#endif maxspd_mod = 1; maxspd_mod *= PM_check_keepaway(); - maxspd_mod *= autocvar_g_movement_highspeed; + maxspd_mod *= PHYS_HIGHSPEED; // fix physics stats for g_movement_highspeed // TODO maybe rather use maxairspeed? needs testing +#ifdef SVQC self.stat_sv_airaccel_qw = AdjustAirAccelQW(autocvar_sv_airaccel_qw, maxspd_mod); if(autocvar_sv_airstrafeaccel_qw) self.stat_sv_airstrafeaccel_qw = AdjustAirAccelQW(autocvar_sv_airstrafeaccel_qw, maxspd_mod); @@ -1753,96 +1596,122 @@ void PM_Main(entity s) self.stat_sv_airspeedlimit_nonqw = autocvar_sv_airspeedlimit_nonqw * maxspd_mod; self.stat_sv_maxspeed = autocvar_sv_maxspeed * maxspd_mod; // also slow walking self.stat_movement_highspeed = autocvar_g_movement_highspeed; - +#endif +#ifdef SVQC if(self.PlayerPhysplug) if(self.PlayerPhysplug()) return; +#endif PM_check_race_movetime(); - +#ifdef SVQC anticheat_physics(); - - buttons = self.BUTTON_ATCK + 2 * self.BUTTON_JUMP + 4 * self.BUTTON_ATCK2 + 8 * self.BUTTON_ZOOM + 16 * self.BUTTON_CROUCH + 32 * self.BUTTON_HOOK + 64 * self.BUTTON_USE + 128 * (self.movement_x < 0) + 256 * (self.movement_x > 0) + 512 * (self.movement_y < 0) + 1024 * (self.movement_y > 0); +#endif + buttons = PHYS_INPUT_BUTTONS(s); if (PM_check_specialcommand(buttons)) return; - +#ifdef SVQC if(sv_maxidle > 0) { if(buttons != self.buttons_old || self.movement != self.movement_old || self.v_angle != self.v_angle_old) self.parm_idlesince = time; } +#endif buttons_prev = self.buttons_old; self.buttons_old = buttons; - self.movement_old = self.movement; - self.v_angle_old = self.v_angle; + self.movement_old = PHYS_INPUT_MOVEVALUES(s); + self.v_angle_old = PHYS_INPUT_ANGLES(s); PM_check_nickspam(); PM_check_punch(); - +#ifdef SVQC if (IS_BOT_CLIENT(self)) { if(playerdemo_read()) return; bot_think(); } +#endif self.items &= ~IT_USING_JETPACK; - +#ifdef SVQC if(IS_PLAYER(self)) +#endif { +#ifdef SVQC if(self.race_penalty) if(time > self.race_penalty) self.race_penalty = 0; +#endif not_allowed_to_move = 0; +#ifdef SVQC if(self.race_penalty) not_allowed_to_move = 1; +#endif +#ifdef SVQC if(!autocvar_sv_ready_restart_after_countdown) - if(time < game_starttime) - not_allowed_to_move = 1; + if(time < game_starttime) + not_allowed_to_move = 1; +#endif if(not_allowed_to_move) { self.velocity = '0 0 0'; self.movetype = MOVETYPE_NONE; +#ifdef SVQC self.disableclientprediction = 2; +#endif } +#ifdef SVQC else if(self.disableclientprediction == 2) { if(self.movetype == MOVETYPE_NONE) self.movetype = MOVETYPE_WALK; self.disableclientprediction = 0; } +#endif } +#ifdef SVQC if (self.movetype == MOVETYPE_NONE) return; +#endif +#ifdef SVQC // when we get here, disableclientprediction cannot be 2 self.disableclientprediction = 0; if(time < self.ladder_time) self.disableclientprediction = 1; +#endif PM_check_spider(); PM_check_frozen(); +#ifdef SVQC MUTATOR_CALLHOOK(PlayerPhysics); +#endif PM_check_blocked(); maxspd_mod = 1; +#ifdef SVQC if(self.in_swamp) { maxspd_mod *= self.swamp_slowdown; //cvar("g_balance_swamp_moverate"); } +#endif +#ifdef SVQC // conveyors: first fix velocity if(self.conveyor.state) self.velocity -= self.conveyor.movedir; +#endif +#ifdef SVQC if (!IS_PLAYER(self)) { maxspd_mod *= autocvar_sv_spectator_speed_multiplier; @@ -1865,24 +1734,29 @@ void PM_Main(entity s) } maxspd_mod *= self.spectatorspeed; } +#endif +#ifdef SVQC // if dead, behave differently if (self.deadflag) goto end; +#endif +#ifdef SVQC if (!self.fixangle && !g_bugrigs) { self.angles_x = 0; self.angles_y = self.v_angle_y; self.angles_z = 0; } +#endif if(self.flags & FL_ONGROUND) +#ifdef SVQC if(IS_PLAYER(self)) // no fall sounds for observers thank you very much if(self.wasFlying) { self.wasFlying = 0; - if(self.waterlevel < WATERLEVEL_SWIMMING) if(time >= self.ladder_time) if (!self.hook) @@ -1899,75 +1773,91 @@ void PM_Main(entity s) } } } +#endif if(IsFlying(self)) self.wasFlying = 1; +#ifdef SVQC if(IS_PLAYER(self)) CheckPlayerJump(); #endif -#ifdef CSQC - if (s.waterlevel >= WATERLEVEL_SWIMMING) - CSQC_ClientMovement_Physics_Swim(s); - else - CSQC_ClientMovement_Physics_Walk(s); -#endif -#ifdef SVQC - if (self.flags & FL_WATERJUMP ) + + if (self.flags & /* FL_WATERJUMP */ 2048) { self.velocity_x = self.movedir_x; self.velocity_y = self.movedir_y; if (time > self.teleport_time || self.waterlevel == WATERLEVEL_NONE) { - self.flags &= ~FL_WATERJUMP; + self.flags &= ~/* FL_WATERJUMP */ 2048; self.teleport_time = 0; } } + +#ifdef SVQC else if (g_bugrigs && IS_PLAYER(self)) - { RaceCarPhysics(); - } +#endif + else if (self.movetype == MOVETYPE_NOCLIP || self.movetype == MOVETYPE_FLY || self.movetype == MOVETYPE_FLY_WORLDONLY) - { PM_fly(maxspd_mod); - } + else if (self.waterlevel >= WATERLEVEL_SWIMMING) - { PM_swim(maxspd_mod); - } + else if (time < self.ladder_time) - { PM_ladder(maxspd_mod); - } + +#ifdef SVQC else if ((self.items & IT_JETPACK) && self.BUTTON_HOOK && (!autocvar_g_jetpack_fuel || self.ammo_fuel >= 0.01 || self.items & IT_UNLIMITED_WEAPON_AMMO) && !self.frozen) - { PM_jetpack(maxspd_mod); - } - else if (self.flags & FL_ONGROUND) - { - PM_walk(buttons_prev, maxspd_mod); - } - else +#endif + + else // TODO: eliminate { - PM_air(buttons_prev, maxspd_mod); - } +#ifdef CSQC + // 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 #endif + if (IS_ONGROUND(self)) + { + PM_walk(buttons_prev, maxspd_mod); + } + else + { + PM_air(buttons_prev, maxspd_mod); + } + } + #ifdef SVQC if(!IS_OBSERVER(self)) PM_check_race(); - +#endif PM_check_vortex(); + :end if(self.flags & FL_ONGROUND) self.lastground = time; +#ifdef SVQC // conveyors: then break velocity again if(self.conveyor.state) self.velocity += self.conveyor.movedir; +#endif self.lastflags = self.flags; self.lastclassname = self.classname; -#endif } void CSQC_ClientMovement_PlayerMove_Frame(entity s) @@ -2032,6 +1922,7 @@ void CSQC_ClientMovement_PlayerMove_Frame(entity s) #undef PHYS_ENTGRAVITY #undef PHYS_FRICTION #undef PHYS_GRAVITY +#undef PHYS_HIGHSPEED #undef PHYS_JUMPVELOCITY #undef PHYS_MAXAIRSPEED #undef PHYS_MAXAIRSTRAFESPEED