X-Git-Url: http://de.git.xonotic.org/?p=xonotic%2Fdarkplaces.git;a=blobdiff_plain;f=cl_input.c;h=e77d22f44252f71fc09f5ded23c9bd5d86c15ad5;hp=6aaf8139353171031b46ee7c500bd42a8c41a237;hb=9ba529f6b88337d1f8ca249a66dc060917e3c3e8;hpb=fb50a9a778603419fe35c6c6fbb018b3bcff7783 diff --git a/cl_input.c b/cl_input.c index 6aaf8139..e77d22f4 100644 --- a/cl_input.c +++ b/cl_input.c @@ -24,6 +24,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "quakedef.h" #include "csprogs.h" +#include "thread.h" /* =============================================================================== @@ -61,7 +62,7 @@ int in_impulse; -void KeyDown (kbutton_t *b) +static void KeyDown (kbutton_t *b) { int k; const char *c; @@ -90,7 +91,7 @@ void KeyDown (kbutton_t *b) b->state |= 1 + 2; // down + impulse down } -void KeyUp (kbutton_t *b) +static void KeyUp (kbutton_t *b) { int k; const char *c; @@ -120,86 +121,86 @@ void KeyUp (kbutton_t *b) b->state |= 4; // impulse up } -void IN_KLookDown (void) {KeyDown(&in_klook);} -void IN_KLookUp (void) {KeyUp(&in_klook);} -void IN_MLookDown (void) {KeyDown(&in_mlook);} -void IN_MLookUp (void) +static void IN_KLookDown (void) {KeyDown(&in_klook);} +static void IN_KLookUp (void) {KeyUp(&in_klook);} +static void IN_MLookDown (void) {KeyDown(&in_mlook);} +static void IN_MLookUp (void) { KeyUp(&in_mlook); if ( !(in_mlook.state&1) && lookspring.value) V_StartPitchDrift(); } -void IN_UpDown(void) {KeyDown(&in_up);} -void IN_UpUp(void) {KeyUp(&in_up);} -void IN_DownDown(void) {KeyDown(&in_down);} -void IN_DownUp(void) {KeyUp(&in_down);} -void IN_LeftDown(void) {KeyDown(&in_left);} -void IN_LeftUp(void) {KeyUp(&in_left);} -void IN_RightDown(void) {KeyDown(&in_right);} -void IN_RightUp(void) {KeyUp(&in_right);} -void IN_ForwardDown(void) {KeyDown(&in_forward);} -void IN_ForwardUp(void) {KeyUp(&in_forward);} -void IN_BackDown(void) {KeyDown(&in_back);} -void IN_BackUp(void) {KeyUp(&in_back);} -void IN_LookupDown(void) {KeyDown(&in_lookup);} -void IN_LookupUp(void) {KeyUp(&in_lookup);} -void IN_LookdownDown(void) {KeyDown(&in_lookdown);} -void IN_LookdownUp(void) {KeyUp(&in_lookdown);} -void IN_MoveleftDown(void) {KeyDown(&in_moveleft);} -void IN_MoveleftUp(void) {KeyUp(&in_moveleft);} -void IN_MoverightDown(void) {KeyDown(&in_moveright);} -void IN_MoverightUp(void) {KeyUp(&in_moveright);} - -void IN_SpeedDown(void) {KeyDown(&in_speed);} -void IN_SpeedUp(void) {KeyUp(&in_speed);} -void IN_StrafeDown(void) {KeyDown(&in_strafe);} -void IN_StrafeUp(void) {KeyUp(&in_strafe);} - -void IN_AttackDown(void) {KeyDown(&in_attack);} -void IN_AttackUp(void) {KeyUp(&in_attack);} - -void IN_UseDown(void) {KeyDown(&in_use);} -void IN_UseUp(void) {KeyUp(&in_use);} +static void IN_UpDown(void) {KeyDown(&in_up);} +static void IN_UpUp(void) {KeyUp(&in_up);} +static void IN_DownDown(void) {KeyDown(&in_down);} +static void IN_DownUp(void) {KeyUp(&in_down);} +static void IN_LeftDown(void) {KeyDown(&in_left);} +static void IN_LeftUp(void) {KeyUp(&in_left);} +static void IN_RightDown(void) {KeyDown(&in_right);} +static void IN_RightUp(void) {KeyUp(&in_right);} +static void IN_ForwardDown(void) {KeyDown(&in_forward);} +static void IN_ForwardUp(void) {KeyUp(&in_forward);} +static void IN_BackDown(void) {KeyDown(&in_back);} +static void IN_BackUp(void) {KeyUp(&in_back);} +static void IN_LookupDown(void) {KeyDown(&in_lookup);} +static void IN_LookupUp(void) {KeyUp(&in_lookup);} +static void IN_LookdownDown(void) {KeyDown(&in_lookdown);} +static void IN_LookdownUp(void) {KeyUp(&in_lookdown);} +static void IN_MoveleftDown(void) {KeyDown(&in_moveleft);} +static void IN_MoveleftUp(void) {KeyUp(&in_moveleft);} +static void IN_MoverightDown(void) {KeyDown(&in_moveright);} +static void IN_MoverightUp(void) {KeyUp(&in_moveright);} + +static void IN_SpeedDown(void) {KeyDown(&in_speed);} +static void IN_SpeedUp(void) {KeyUp(&in_speed);} +static void IN_StrafeDown(void) {KeyDown(&in_strafe);} +static void IN_StrafeUp(void) {KeyUp(&in_strafe);} + +static void IN_AttackDown(void) {KeyDown(&in_attack);} +static void IN_AttackUp(void) {KeyUp(&in_attack);} + +static void IN_UseDown(void) {KeyDown(&in_use);} +static void IN_UseUp(void) {KeyUp(&in_use);} // LordHavoc: added 6 new buttons -void IN_Button3Down(void) {KeyDown(&in_button3);} -void IN_Button3Up(void) {KeyUp(&in_button3);} -void IN_Button4Down(void) {KeyDown(&in_button4);} -void IN_Button4Up(void) {KeyUp(&in_button4);} -void IN_Button5Down(void) {KeyDown(&in_button5);} -void IN_Button5Up(void) {KeyUp(&in_button5);} -void IN_Button6Down(void) {KeyDown(&in_button6);} -void IN_Button6Up(void) {KeyUp(&in_button6);} -void IN_Button7Down(void) {KeyDown(&in_button7);} -void IN_Button7Up(void) {KeyUp(&in_button7);} -void IN_Button8Down(void) {KeyDown(&in_button8);} -void IN_Button8Up(void) {KeyUp(&in_button8);} - -void IN_Button9Down(void) {KeyDown(&in_button9);} -void IN_Button9Up(void) {KeyUp(&in_button9);} -void IN_Button10Down(void) {KeyDown(&in_button10);} -void IN_Button10Up(void) {KeyUp(&in_button10);} -void IN_Button11Down(void) {KeyDown(&in_button11);} -void IN_Button11Up(void) {KeyUp(&in_button11);} -void IN_Button12Down(void) {KeyDown(&in_button12);} -void IN_Button12Up(void) {KeyUp(&in_button12);} -void IN_Button13Down(void) {KeyDown(&in_button13);} -void IN_Button13Up(void) {KeyUp(&in_button13);} -void IN_Button14Down(void) {KeyDown(&in_button14);} -void IN_Button14Up(void) {KeyUp(&in_button14);} -void IN_Button15Down(void) {KeyDown(&in_button15);} -void IN_Button15Up(void) {KeyUp(&in_button15);} -void IN_Button16Down(void) {KeyDown(&in_button16);} -void IN_Button16Up(void) {KeyUp(&in_button16);} - -void IN_JumpDown (void) {KeyDown(&in_jump);} -void IN_JumpUp (void) {KeyUp(&in_jump);} - -void IN_Impulse (void) {in_impulse=atoi(Cmd_Argv(1));} +static void IN_Button3Down(void) {KeyDown(&in_button3);} +static void IN_Button3Up(void) {KeyUp(&in_button3);} +static void IN_Button4Down(void) {KeyDown(&in_button4);} +static void IN_Button4Up(void) {KeyUp(&in_button4);} +static void IN_Button5Down(void) {KeyDown(&in_button5);} +static void IN_Button5Up(void) {KeyUp(&in_button5);} +static void IN_Button6Down(void) {KeyDown(&in_button6);} +static void IN_Button6Up(void) {KeyUp(&in_button6);} +static void IN_Button7Down(void) {KeyDown(&in_button7);} +static void IN_Button7Up(void) {KeyUp(&in_button7);} +static void IN_Button8Down(void) {KeyDown(&in_button8);} +static void IN_Button8Up(void) {KeyUp(&in_button8);} + +static void IN_Button9Down(void) {KeyDown(&in_button9);} +static void IN_Button9Up(void) {KeyUp(&in_button9);} +static void IN_Button10Down(void) {KeyDown(&in_button10);} +static void IN_Button10Up(void) {KeyUp(&in_button10);} +static void IN_Button11Down(void) {KeyDown(&in_button11);} +static void IN_Button11Up(void) {KeyUp(&in_button11);} +static void IN_Button12Down(void) {KeyDown(&in_button12);} +static void IN_Button12Up(void) {KeyUp(&in_button12);} +static void IN_Button13Down(void) {KeyDown(&in_button13);} +static void IN_Button13Up(void) {KeyUp(&in_button13);} +static void IN_Button14Down(void) {KeyDown(&in_button14);} +static void IN_Button14Up(void) {KeyUp(&in_button14);} +static void IN_Button15Down(void) {KeyDown(&in_button15);} +static void IN_Button15Up(void) {KeyUp(&in_button15);} +static void IN_Button16Down(void) {KeyDown(&in_button16);} +static void IN_Button16Up(void) {KeyUp(&in_button16);} + +static void IN_JumpDown (void) {KeyDown(&in_jump);} +static void IN_JumpUp (void) {KeyUp(&in_jump);} + +static void IN_Impulse (void) {in_impulse=atoi(Cmd_Argv(1));} in_bestweapon_info_t in_bestweapon_info[IN_BESTWEAPON_MAX]; -void IN_BestWeapon_Register(const char *name, int impulse, int weaponbit, int activeweaponcode, int ammostat, int ammomin) +static void IN_BestWeapon_Register(const char *name, int impulse, int weaponbit, int activeweaponcode, int ammostat, int ammomin) { int i; for(i = 0; i < IN_BESTWEAPON_MAX && in_bestweapon_info[i].impulse; ++i) @@ -240,7 +241,7 @@ void IN_BestWeapon_ResetData (void) IN_BestWeapon_Register("h", 226, HIT_MJOLNIR, HIT_MJOLNIR, STAT_CELLS, 0); // hipnotic mjolnir hammer } -void IN_BestWeapon_Register_f (void) +static void IN_BestWeapon_Register_f (void) { if(Cmd_Argc() == 7) { @@ -267,7 +268,7 @@ void IN_BestWeapon_Register_f (void) } } -void IN_BestWeapon (void) +static void IN_BestWeapon (void) { int i, n; const char *t; @@ -422,7 +423,8 @@ cvar_t cl_pitchspeed = {CVAR_SAVE, "cl_pitchspeed","150","keyboard pitch turning cvar_t cl_anglespeedkey = {CVAR_SAVE, "cl_anglespeedkey","1.5","how much +speed multiplies keyboard turning speed"}; -cvar_t cl_movement = {CVAR_SAVE, "cl_movement", "0", "enables clientside prediction of your player movement"}; +cvar_t cl_movement = {CVAR_SAVE, "cl_movement", "0", "enables clientside prediction of your player movement on DP servers (use cl_nopred for QWSV servers)"}; +cvar_t cl_movement_replay = {0, "cl_movement_replay", "1", "use engine prediction"}; cvar_t cl_movement_nettimeout = {CVAR_SAVE, "cl_movement_nettimeout", "0.3", "stops predicting moves when server is lagging badly (avoids major performance problems), timeout in seconds"}; cvar_t cl_movement_minping = {CVAR_SAVE, "cl_movement_minping", "0", "whether to use prediction when ping is lower than this value in milliseconds"}; cvar_t cl_movement_track_canjump = {CVAR_SAVE, "cl_movement_track_canjump", "1", "track if the player released the jump key between two jumps to decide if he is able to jump or not; when off, this causes some \"sliding\" slightly above the floor when the jump key is held too long; if the mod allows repeated jumping by holding space all the time, this has to be set to zero too"}; @@ -440,9 +442,10 @@ cvar_t cl_movement_wateraccelerate = {0, "cl_movement_wateraccelerate", "-1", "h cvar_t cl_movement_jumpvelocity = {0, "cl_movement_jumpvelocity", "270", "how fast you move upward when you begin a jump (should match the quakec code)"}; cvar_t cl_movement_airaccel_qw = {0, "cl_movement_airaccel_qw", "1", "ratio of QW-style air control as opposed to simple acceleration (reduces speed gain when zigzagging) (should match sv_airaccel_qw); when < 0, the speed is clamped against the maximum allowed forward speed after the move"}; cvar_t cl_movement_airaccel_sideways_friction = {0, "cl_movement_airaccel_sideways_friction", "0", "anti-sideways movement stabilization (should match sv_airaccel_sideways_friction); when < 0, only so much friction is applied that braking (by accelerating backwards) cannot be stronger"}; +cvar_t cl_nopred = {CVAR_SAVE, "cl_nopred", "0", "(QWSV only) disables player movement prediction when playing on QWSV servers (this setting is separate from cl_movement because player expectations are different when playing on DP vs QW servers)"}; -cvar_t in_pitch_min = {0, "in_pitch_min", "-90", "how far downward you can aim (quake used -70"}; -cvar_t in_pitch_max = {0, "in_pitch_max", "90", "how far upward you can aim (quake used 80"}; +cvar_t in_pitch_min = {0, "in_pitch_min", "-90", "how far you can aim upward (quake used -70)"}; +cvar_t in_pitch_max = {0, "in_pitch_max", "90", "how far you can aim downward (quake used 80)"}; cvar_t m_filter = {CVAR_SAVE, "m_filter","0", "smoothes mouse movement, less responsive but smoother aiming"}; cvar_t m_accelerate = {CVAR_SAVE, "m_accelerate","1", "mouse acceleration factor (try 2)"}; @@ -456,6 +459,8 @@ cvar_t cl_netimmediatebuttons = {CVAR_SAVE, "cl_netimmediatebuttons", "1", "send cvar_t cl_nodelta = {0, "cl_nodelta", "0", "disables delta compression of non-player entities in QW network protocol"}; +cvar_t cl_csqc_generatemousemoveevents = {0, "cl_csqc_generatemousemoveevents", "1", "enables calls to CSQC_InputEvent with type 2, for compliance with EXT_CSQC spec"}; + extern cvar_t v_flipped; /* @@ -465,7 +470,7 @@ CL_AdjustAngles Moves the local angle positions ================ */ -void CL_AdjustAngles (void) +static void CL_AdjustAngles (void) { float speed; float up, down; @@ -502,6 +507,8 @@ void CL_AdjustAngles (void) cl.viewangles[YAW] -= 360; if (cl.viewangles[PITCH] >= 180) cl.viewangles[PITCH] -= 360; + // TODO: honor serverinfo minpitch and maxpitch values in PROTOCOL_QUAKEWORLD + // TODO: honor proquake pq_fullpitch cvar when playing on proquake server (server stuffcmd's this to 0 usually) cl.viewangles[PITCH] = bound(in_pitch_min.value, cl.viewangles[PITCH], in_pitch_max.value); cl.viewangles[ROLL] = bound(-180, cl.viewangles[ROLL], 180); } @@ -561,6 +568,27 @@ void CL_Input (void) // allow mice or other external controllers to add to the move IN_Move (); + // send mouse move to csqc + if (cl.csqc_loaded && cl_csqc_generatemousemoveevents.integer) + { + if (cl.csqc_wantsmousemove) + { + // event type 3 is a DP_CSQC thing + static int oldwindowmouse[2]; + if (oldwindowmouse[0] != in_windowmouse_x || oldwindowmouse[1] != in_windowmouse_y) + { + CL_VM_InputEvent(3, in_windowmouse_x * vid_conwidth.value / vid.width, in_windowmouse_y * vid_conheight.value / vid.height); + oldwindowmouse[0] = in_windowmouse_x; + oldwindowmouse[1] = in_windowmouse_y; + } + } + else + { + if (in_mouse_x || in_mouse_y) + CL_VM_InputEvent(2, in_mouse_x, in_mouse_y); + } + } + // apply m_accelerate if it is on if(m_accelerate.value > 1) { @@ -587,14 +615,7 @@ void CL_Input (void) } else { - /* - f = log(averagespeed); - mi = log(mi); - ma = log(ma); - */ f = averagespeed; - mi = mi; - ma = ma; f = (f - mi) / (ma - mi) * (m_accelerate.value - 1) + 1; } @@ -622,17 +643,10 @@ void CL_Input (void) } // if not in menu, apply mouse move to viewangles/movement - if (!key_consoleactive && key_dest == key_game && !cl.csqc_wantsmousemove) + if (!key_consoleactive && key_dest == key_game && !cl.csqc_wantsmousemove && cl_prydoncursor.integer <= 0) { float modulatedsensitivity = sensitivity.value * cl.sensitivityscale; - if (cl_prydoncursor.integer > 0) - { - // mouse interacting with the scene, mostly stationary view - V_StopPitchDrift(); - cl.cmd.cursor_screen[0] += in_mouse_x * modulatedsensitivity / vid.width; - cl.cmd.cursor_screen[1] += in_mouse_y * modulatedsensitivity / vid.height; - } - else if (in_strafe.state & 1) + if (in_strafe.state & 1) { // strafing mode, all looking is movement V_StopPitchDrift(); @@ -660,7 +674,13 @@ void CL_Input (void) } } else // don't pitch drift when csqc is controlling the mouse + { + // mouse interacting with the scene, mostly stationary view V_StopPitchDrift(); + // update prydon cursor + cl.cmd.cursor_screen[0] = in_windowmouse_x * 2.0 / vid.width - 1.0; + cl.cmd.cursor_screen[1] = in_windowmouse_y * 2.0 / vid.height - 1.0; + } if(v_flipped.integer) { @@ -680,7 +700,6 @@ void CL_Input (void) { // digital direction, analog amount vec_t wishvel_x, wishvel_y; - f *= max(cl_sidespeed.value, max(cl_forwardspeed.value, cl_backspeed.value)); wishvel_x = fabs(cl.cmd.forwardmove); wishvel_y = fabs(cl.cmd.sidemove); if(wishvel_x != 0 && wishvel_y != 0 && wishvel_x != wishvel_y) @@ -739,7 +758,7 @@ void CL_Input (void) #include "cl_collision.h" -void CL_UpdatePrydonCursor(void) +static void CL_UpdatePrydonCursor(void) { vec3_t temp; @@ -789,40 +808,6 @@ void CL_UpdatePrydonCursor(void) cl.cmd.cursor_fraction = CL_SelectTraceLine(cl.cmd.cursor_start, cl.cmd.cursor_end, cl.cmd.cursor_impact, cl.cmd.cursor_normal, &cl.cmd.cursor_entitynumber, (chase_active.integer || cl.intermission) ? &cl.entities[cl.playerentity].render : NULL); } -typedef enum waterlevel_e -{ - WATERLEVEL_NONE, - WATERLEVEL_WETFEET, - WATERLEVEL_SWIMMING, - WATERLEVEL_SUBMERGED -} -waterlevel_t; - -typedef struct cl_clientmovement_state_s -{ - // position - vec3_t origin; - vec3_t velocity; - // current bounding box (different if crouched vs standing) - vec3_t mins; - vec3_t maxs; - // currently on the ground - qboolean onground; - // currently crouching - qboolean crouched; - // what kind of water (SUPERCONTENTS_LAVA for instance) - int watertype; - // how deep - waterlevel_t waterlevel; - // weird hacks when jumping out of water - // (this is in seconds and counts down to 0) - float waterjumptime; - - // user command - usercmd_t cmd; -} -cl_clientmovement_state_t; - #define NUMOFFSETS 27 static vec3_t offsets[NUMOFFSETS] = { @@ -847,14 +832,14 @@ static vec3_t offsets[NUMOFFSETS] = {-0.125, 0.125, -0.125}, { 0.125, 0.125, -0.125}, }; -qboolean CL_ClientMovement_Unstick(cl_clientmovement_state_t *s) +static qboolean CL_ClientMovement_Unstick(cl_clientmovement_state_t *s) { int i; vec3_t neworigin; for (i = 0;i < NUMOFFSETS;i++) { VectorAdd(offsets[i], s->origin, neworigin); - if (!CL_TraceBox(neworigin, cl.playercrouchmins, cl.playercrouchmaxs, neworigin, MOVE_NORMAL, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_PLAYERCLIP, true, true, NULL, false).startsolid) + if (!CL_TraceBox(neworigin, cl.playercrouchmins, cl.playercrouchmaxs, neworigin, MOVE_NORMAL, s->self, SUPERCONTENTS_SOLID | SUPERCONTENTS_PLAYERCLIP, 0, collision_extendmovelength.value, true, true, NULL, true).startsolid) { VectorCopy(neworigin, s->origin); return true; @@ -864,8 +849,9 @@ qboolean CL_ClientMovement_Unstick(cl_clientmovement_state_t *s) return false; } -void CL_ClientMovement_UpdateStatus(cl_clientmovement_state_t *s) +static void CL_ClientMovement_UpdateStatus(cl_clientmovement_state_t *s) { + vec_t f; vec3_t origin1, origin2; trace_t trace; @@ -885,7 +871,7 @@ void CL_ClientMovement_UpdateStatus(cl_clientmovement_state_t *s) // low ceiling first if (s->crouched) { - trace = CL_TraceBox(s->origin, cl.playerstandmins, cl.playerstandmaxs, s->origin, MOVE_NORMAL, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_PLAYERCLIP, true, true, NULL, false); + trace = CL_TraceBox(s->origin, cl.playerstandmins, cl.playerstandmaxs, s->origin, MOVE_NORMAL, s->self, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_PLAYERCLIP, 0, collision_extendmovelength.value, true, true, NULL, true); if (!trace.startsolid) s->crouched = false; } @@ -904,22 +890,32 @@ void CL_ClientMovement_UpdateStatus(cl_clientmovement_state_t *s) // set onground VectorSet(origin1, s->origin[0], s->origin[1], s->origin[2] + 1); VectorSet(origin2, s->origin[0], s->origin[1], s->origin[2] - 1); // -2 causes clientside doublejump bug at above 150fps, raising that to 300fps :) - trace = CL_TraceBox(origin1, s->mins, s->maxs, origin2, MOVE_NORMAL, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_PLAYERCLIP, true, true, NULL, false); - s->onground = trace.fraction < 1 && trace.plane.normal[2] > 0.7; + trace = CL_TraceBox(origin1, s->mins, s->maxs, origin2, MOVE_NORMAL, s->self, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_PLAYERCLIP, 0, collision_extendmovelength.value, true, true, NULL, true); + if(trace.fraction < 1 && trace.plane.normal[2] > 0.7) + { + s->onground = true; + + // 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 + VectorMA(s->velocity, -f, trace.plane.normal, s->velocity); + } + else + s->onground = false; // set watertype/waterlevel VectorSet(origin1, s->origin[0], s->origin[1], s->origin[2] + s->mins[2] + 1); s->waterlevel = WATERLEVEL_NONE; - s->watertype = CL_TracePoint(origin1, MOVE_NOMONSTERS, NULL, 0, true, false, NULL, false).startsupercontents & SUPERCONTENTS_LIQUIDSMASK; + s->watertype = CL_TracePoint(origin1, MOVE_NOMONSTERS, s->self, 0, 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, NULL, 0, true, false, NULL, false).startsupercontents & SUPERCONTENTS_LIQUIDSMASK) + if (CL_TracePoint(origin1, MOVE_NOMONSTERS, s->self, 0, 0, true, false, NULL, false).startsupercontents & SUPERCONTENTS_LIQUIDSMASK) { s->waterlevel = WATERLEVEL_SWIMMING; origin1[2] = s->origin[2] + 22; - if (CL_TracePoint(origin1, MOVE_NOMONSTERS, NULL, 0, true, false, NULL, false).startsupercontents & SUPERCONTENTS_LIQUIDSMASK) + if (CL_TracePoint(origin1, MOVE_NOMONSTERS, s->self, 0, 0, true, false, NULL, false).startsupercontents & SUPERCONTENTS_LIQUIDSMASK) s->waterlevel = WATERLEVEL_SUBMERGED; } } @@ -929,7 +925,7 @@ void CL_ClientMovement_UpdateStatus(cl_clientmovement_state_t *s) s->waterjumptime = 0; } -void CL_ClientMovement_Move(cl_clientmovement_state_t *s) +static void CL_ClientMovement_Move(cl_clientmovement_state_t *s) { int bump; double t; @@ -946,20 +942,20 @@ void CL_ClientMovement_Move(cl_clientmovement_state_t *s) for (bump = 0, t = s->cmd.frametime;bump < 8 && VectorLength2(s->velocity) > 0;bump++) { VectorMA(s->origin, t, s->velocity, neworigin); - trace = CL_TraceBox(s->origin, s->mins, s->maxs, neworigin, MOVE_NORMAL, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_PLAYERCLIP, true, true, NULL, false); + trace = CL_TraceBox(s->origin, s->mins, s->maxs, neworigin, MOVE_NORMAL, s->self, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_PLAYERCLIP, 0, collision_extendmovelength.value, true, true, NULL, true); if (trace.fraction < 1 && trace.plane.normal[2] == 0) { // may be a step or wall, try stepping up // first move forward at a higher level VectorSet(currentorigin2, s->origin[0], s->origin[1], s->origin[2] + cl.movevars_stepheight); VectorSet(neworigin2, neworigin[0], neworigin[1], s->origin[2] + cl.movevars_stepheight); - trace2 = CL_TraceBox(currentorigin2, s->mins, s->maxs, neworigin2, MOVE_NORMAL, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_PLAYERCLIP, true, true, NULL, false); + trace2 = CL_TraceBox(currentorigin2, s->mins, s->maxs, neworigin2, MOVE_NORMAL, s->self, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_PLAYERCLIP, 0, collision_extendmovelength.value, true, true, NULL, true); if (!trace2.startsolid) { // then move down from there VectorCopy(trace2.endpos, currentorigin2); VectorSet(neworigin2, trace2.endpos[0], trace2.endpos[1], s->origin[2]); - trace3 = CL_TraceBox(currentorigin2, s->mins, s->maxs, neworigin2, MOVE_NORMAL, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_PLAYERCLIP, true, true, NULL, false); + trace3 = CL_TraceBox(currentorigin2, s->mins, s->maxs, neworigin2, MOVE_NORMAL, s->self, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_PLAYERCLIP, 0, collision_extendmovelength.value, true, true, NULL, true); //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(trace3.endpos[0] - trace.endpos[0]) >= 0.03125 || fabs(trace3.endpos[1] - trace.endpos[1]) >= 0.03125) @@ -978,8 +974,12 @@ void CL_ClientMovement_Move(cl_clientmovement_state_t *s) if (trace.fraction == 1) break; - //if (trace.plane.normal[2] > 0.7) - // s->onground = true; + // this is only really needed for nogravityonground combined with gravityunaffectedbyticrate + // 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[2] > 0.7) + s->onground = true; t -= t * trace.fraction; @@ -991,7 +991,7 @@ void CL_ClientMovement_Move(cl_clientmovement_state_t *s) } -void CL_ClientMovement_Physics_Swim(cl_clientmovement_state_t *s) +static void CL_ClientMovement_Physics_Swim(cl_clientmovement_state_t *s) { vec_t wishspeed; vec_t f; @@ -1009,10 +1009,10 @@ void CL_ClientMovement_Physics_Swim(cl_clientmovement_state_t *s) AngleVectors(yawangles, forward, NULL, NULL); VectorMA(s->origin, 24, forward, spot); spot[2] += 8; - if (CL_TracePoint(spot, MOVE_NOMONSTERS, NULL, 0, true, false, NULL, false).startsolid) + if (CL_TracePoint(spot, MOVE_NOMONSTERS, s->self, 0, 0, true, false, NULL, false).startsolid) { spot[2] += 24; - if (!CL_TracePoint(spot, MOVE_NOMONSTERS, NULL, 0, true, false, NULL, false).startsolid) + if (!CL_TracePoint(spot, MOVE_NOMONSTERS, s->self, 0, 0, true, false, NULL, false).startsolid) { VectorScale(forward, 50, s->velocity); s->velocity[2] = 310; @@ -1075,7 +1075,7 @@ void CL_ClientMovement_Physics_Swim(cl_clientmovement_state_t *s) s->velocity[2] = 80; else { - if (gamemode == GAME_NEXUIZ) + if (IS_NEXUIZ_DERIVED(gamemode)) s->velocity[2] = 200; else s->velocity[2] = 100; @@ -1099,7 +1099,26 @@ static vec_t CL_IsMoveInDirection(vec_t forward, vec_t side, vec_t angle) return 1 - fabs(angle); } -void CL_ClientMovement_Physics_CPM_PM_Aircontrol(cl_clientmovement_state_t *s, vec3_t wishdir, vec_t wishspeed) +static vec_t CL_GeomLerp(vec_t a, vec_t lerp, vec_t 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); +} + +static void CL_ClientMovement_Physics_CPM_PM_Aircontrol(cl_clientmovement_state_t *s, vec3_t wishdir, vec_t wishspeed) { vec_t zspeed, speed, dot, k; @@ -1121,9 +1140,11 @@ void CL_ClientMovement_Physics_CPM_PM_Aircontrol(cl_clientmovement_state_t *s, v speed = VectorNormalizeLength(s->velocity); dot = DotProduct(s->velocity, wishdir); - k *= cl.movevars_aircontrol*dot*dot*s->cmd.frametime; if(dot > 0) { // we can't change direction while slowing down + k *= pow(dot, cl.movevars_aircontrol_power)*s->cmd.frametime; + speed = max(0, speed - cl.movevars_aircontrol_penalty * sqrt(max(0, 1 - dot*dot)) * k/32); + k *= cl.movevars_aircontrol; VectorMAM(speed, s->velocity, k, wishdir, s->velocity); VectorNormalize(s->velocity); } @@ -1132,7 +1153,15 @@ void CL_ClientMovement_Physics_CPM_PM_Aircontrol(cl_clientmovement_state_t *s, v s->velocity[2] = zspeed; } -void CL_ClientMovement_Physics_PM_Accelerate(cl_clientmovement_state_t *s, vec3_t wishdir, vec_t wishspeed, vec_t wishspeed0, vec_t accel, vec_t accelqw, vec_t sidefric) +static float CL_ClientMovement_Physics_AdjustAirAccelQW(float accelqw, float factor) +{ + return + (accelqw < 0 ? -1 : +1) + * + bound(0.000001, 1 - (1 - fabs(accelqw)) * factor, 1); +} + +static void CL_ClientMovement_Physics_PM_Accelerate(cl_clientmovement_state_t *s, vec3_t wishdir, vec_t wishspeed, vec_t wishspeed0, vec_t accel, vec_t accelqw, vec_t stretchfactor, vec_t sidefric, vec_t speedlimit) { vec_t vel_straight; vec_t vel_z; @@ -1141,10 +1170,16 @@ void CL_ClientMovement_Physics_PM_Accelerate(cl_clientmovement_state_t *s, vec3_ vec3_t vel_xy; vec_t vel_xy_current; vec_t vel_xy_backward, vel_xy_forward; - qboolean speedclamp; + vec_t speedclamp; - speedclamp = (accelqw < 0); - if(speedclamp) + if(stretchfactor > 0) + speedclamp = stretchfactor; + else if(accelqw < 0) + speedclamp = 1; + else + speedclamp = -1; // no clamping + + if(accelqw < 0) accelqw = -accelqw; if(cl.moveflags & MOVEFLAG_Q2AIRACCELERATE) @@ -1158,6 +1193,8 @@ void CL_ClientMovement_Physics_PM_Accelerate(cl_clientmovement_state_t *s, vec3_ step = accel * s->cmd.frametime * wishspeed0; vel_xy_current = VectorLength(vel_xy); + if(speedlimit > 0) + accelqw = CL_ClientMovement_Physics_AdjustAirAccelQW(accelqw, (speedlimit - bound(wishspeed, vel_xy_current, speedlimit)) / max(1, speedlimit - wishspeed)); vel_xy_forward = vel_xy_current + bound(0, wishspeed - vel_xy_current, step) * accelqw + step * (1 - accelqw); vel_xy_backward = vel_xy_current - bound(0, wishspeed + vel_xy_current, step) * accelqw - step * (1 - accelqw); if(vel_xy_backward < 0) @@ -1189,20 +1226,22 @@ void CL_ClientMovement_Physics_PM_Accelerate(cl_clientmovement_state_t *s, vec3_ VectorMA(vel_perpend, vel_straight, wishdir, s->velocity); - if(speedclamp) + if(speedclamp >= 0) { - vel_xy_current = min(VectorLength(s->velocity), vel_xy_forward); - if(vel_xy_current > 0) // prevent division by zero + vec_t vel_xy_preclamp; + vel_xy_preclamp = VectorLength(s->velocity); + if(vel_xy_preclamp > 0) // prevent division by zero { - VectorNormalize(s->velocity); - VectorScale(s->velocity, vel_xy_current, s->velocity); + vel_xy_current += (vel_xy_forward - vel_xy_current) * speedclamp; + if(vel_xy_current < vel_xy_preclamp) + VectorScale(s->velocity, (vel_xy_current / vel_xy_preclamp), s->velocity); } } s->velocity[2] += vel_z; } -void CL_ClientMovement_Physics_PM_AirAccelerate(cl_clientmovement_state_t *s, vec3_t wishdir, vec_t wishspeed) +static void CL_ClientMovement_Physics_PM_AirAccelerate(cl_clientmovement_state_t *s, vec3_t wishdir, vec_t wishspeed) { vec3_t curvel, wishvel, acceldir, curdir; float addspeed, accelspeed, curspeed; @@ -1223,9 +1262,9 @@ void CL_ClientMovement_Physics_PM_AirAccelerate(cl_clientmovement_state_t *s, ve if( wishspeed > curspeed * 1.01f ) { - float accelspeed = curspeed + airforwardaccel * cl.movevars_maxairspeed * s->cmd.frametime; - if( accelspeed < wishspeed ) - wishspeed = accelspeed; + float faccelspeed = curspeed + airforwardaccel * cl.movevars_maxairspeed * s->cmd.frametime; + if( faccelspeed < wishspeed ) + wishspeed = faccelspeed; } else { @@ -1253,13 +1292,14 @@ void CL_ClientMovement_Physics_PM_AirAccelerate(cl_clientmovement_state_t *s, ve VectorMA( s->velocity, accelspeed, acceldir, s->velocity ); } -void CL_ClientMovement_Physics_Walk(cl_clientmovement_state_t *s) +static void CL_ClientMovement_Physics_Walk(cl_clientmovement_state_t *s) { vec_t friction; vec_t wishspeed; vec_t addspeed; vec_t accelspeed; vec_t f; + vec_t gravity; vec3_t forward; vec3_t right; vec3_t up; @@ -1272,7 +1312,7 @@ void CL_ClientMovement_Physics_Walk(cl_clientmovement_state_t *s) // released at least once since the last jump if (s->cmd.jump) { - if (s->onground && (s->cmd.canjump || !cl_movement_track_canjump.integer)) // FIXME remove this cvar again when canjump logic actually works, or maybe keep it for mods that allow "pogo-ing" + if (s->onground && (s->cmd.canjump || !cl_movement_track_canjump.integer)) { s->velocity[2] += cl.movevars_jumpvelocity; s->onground = false; @@ -1315,9 +1355,9 @@ void CL_ClientMovement_Physics_Walk(cl_clientmovement_state_t *s) VectorSet(neworigin2, s->origin[0] + s->velocity[0]*(16/f), s->origin[1] + s->velocity[1]*(16/f), s->origin[2] + s->mins[2]); VectorSet(neworigin3, neworigin2[0], neworigin2[1], neworigin2[2] - 34); if (cls.protocol == PROTOCOL_QUAKEWORLD) - trace = CL_TraceBox(neworigin2, s->mins, s->maxs, neworigin3, MOVE_NORMAL, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_PLAYERCLIP, true, true, NULL, false); + trace = CL_TraceBox(neworigin2, s->mins, s->maxs, neworigin3, MOVE_NORMAL, s->self, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_PLAYERCLIP, 0, collision_extendmovelength.value, true, true, NULL, true); else - trace = CL_TraceLine(neworigin2, neworigin3, MOVE_NORMAL, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_PLAYERCLIP, true, true, NULL, false); + trace = CL_TraceLine(neworigin2, neworigin3, MOVE_NORMAL, s->self, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_PLAYERCLIP, 0, collision_extendmovelength.value, true, true, NULL, true, false); if (trace.fraction == 1 && !trace.startsolid) friction *= cl.movevars_edgefriction; } @@ -1332,18 +1372,30 @@ void CL_ClientMovement_Physics_Walk(cl_clientmovement_state_t *s) accelspeed = min(cl.movevars_accelerate * s->cmd.frametime * wishspeed, addspeed); VectorMA(s->velocity, accelspeed, wishdir, s->velocity); } - s->velocity[2] -= cl.movevars_gravity * cl.movevars_entgravity * s->cmd.frametime; + gravity = cl.movevars_gravity * cl.movevars_entgravity * s->cmd.frametime; + if(!(cl.moveflags & MOVEFLAG_NOGRAVITYONGROUND)) + { + if(cl.moveflags & MOVEFLAG_GRAVITYUNAFFECTEDBYTICRATE) + s->velocity[2] -= gravity * 0.5f; + else + s->velocity[2] -= gravity; + } if (cls.protocol == PROTOCOL_QUAKEWORLD) s->velocity[2] = 0; if (VectorLength2(s->velocity)) CL_ClientMovement_Move(s); + if(!(cl.moveflags & MOVEFLAG_NOGRAVITYONGROUND) || !s->onground) + { + if(cl.moveflags & MOVEFLAG_GRAVITYUNAFFECTEDBYTICRATE) + s->velocity[2] -= gravity * 0.5f; + } } else { if (s->waterjumptime <= 0) { // apply air speed limit - vec_t accel, wishspeed0, wishspeed2, accelqw; + vec_t accel, wishspeed0, wishspeed2, accelqw, strafity; qboolean accelerating; accelqw = cl.movevars_airaccel_qw; @@ -1358,49 +1410,49 @@ void CL_ClientMovement_Physics_Walk(cl_clientmovement_state_t *s) // CPM: air control if(cl.movevars_airstopaccelerate != 0) - if(DotProduct(s->velocity, wishdir) < 0) - accel = cl.movevars_airstopaccelerate; - // this doesn't play well with analog input, but can't really be - // fixed like the AirControl can. So, don't set the maxairstrafe* - // cvars when you want to support analog input. - if(s->cmd.forwardmove == 0 && s->cmd.sidemove != 0) { - if(cl.movevars_maxairstrafespeed) - { - if(wishspeed > cl.movevars_maxairstrafespeed) - wishspeed = cl.movevars_maxairstrafespeed; - if(cl.movevars_maxairstrafespeed < cl.movevars_maxairspeed) - accelqw = 1; - // otherwise, CPMA-style air acceleration misbehaves a lot - // if partially non-QW acceleration is used (as in, strafing - // would get faster than moving forward straight) - } - if(cl.movevars_airstrafeaccelerate) - { - accel = cl.movevars_airstrafeaccelerate; - if(cl.movevars_airstrafeaccelerate > cl.movevars_airaccelerate) - accelqw = 1; - // otherwise, CPMA-style air acceleration misbehaves a lot - // if partially non-QW acceleration is used (as in, strafing - // would get faster than moving forward straight) - } + vec3_t curdir; + curdir[0] = s->velocity[0]; + curdir[1] = s->velocity[1]; + curdir[2] = 0; + VectorNormalize(curdir); + accel = accel + (cl.movevars_airstopaccelerate - accel) * max(0, -DotProduct(curdir, wishdir)); } + strafity = CL_IsMoveInDirection(s->cmd.forwardmove, s->cmd.sidemove, -90) + CL_IsMoveInDirection(s->cmd.forwardmove, s->cmd.sidemove, +90); // if one is nonzero, other is always zero + if(cl.movevars_maxairstrafespeed) + wishspeed = min(wishspeed, CL_GeomLerp(cl.movevars_maxairspeed, strafity, cl.movevars_maxairstrafespeed)); + if(cl.movevars_airstrafeaccelerate) + accel = CL_GeomLerp(cl.movevars_airaccelerate, strafity, cl.movevars_airstrafeaccelerate); + if(cl.movevars_airstrafeaccel_qw) + accelqw = + (((strafity > 0.5 ? cl.movevars_airstrafeaccel_qw : cl.movevars_airaccel_qw) >= 0) ? +1 : -1) + * + (1 - CL_GeomLerp(1 - fabs(cl.movevars_airaccel_qw), strafity, 1 - fabs(cl.movevars_airstrafeaccel_qw))); // !CPM if(cl.movevars_warsowbunny_turnaccel && accelerating && s->cmd.sidemove == 0 && s->cmd.forwardmove != 0) CL_ClientMovement_Physics_PM_AirAccelerate(s, wishdir, wishspeed2); else - CL_ClientMovement_Physics_PM_Accelerate(s, wishdir, wishspeed, wishspeed0, accel, accelqw, cl.movevars_airaccel_sideways_friction / cl.movevars_maxairspeed); + CL_ClientMovement_Physics_PM_Accelerate(s, wishdir, wishspeed, wishspeed0, accel, accelqw, cl.movevars_airaccel_qw_stretchfactor, cl.movevars_airaccel_sideways_friction / cl.movevars_maxairspeed, cl.movevars_airspeedlimit_nonqw); if(cl.movevars_aircontrol) CL_ClientMovement_Physics_CPM_PM_Aircontrol(s, wishdir, wishspeed2); } - s->velocity[2] -= cl.movevars_gravity * cl.movevars_entgravity * s->cmd.frametime; + gravity = cl.movevars_gravity * cl.movevars_entgravity * s->cmd.frametime; + if(cl.moveflags & MOVEFLAG_GRAVITYUNAFFECTEDBYTICRATE) + s->velocity[2] -= gravity * 0.5f; + else + s->velocity[2] -= gravity; CL_ClientMovement_Move(s); + if(!(cl.moveflags & MOVEFLAG_NOGRAVITYONGROUND) || !s->onground) + { + if(cl.moveflags & MOVEFLAG_GRAVITYUNAFFECTEDBYTICRATE) + s->velocity[2] -= gravity * 0.5f; + } } } -void CL_ClientMovement_PlayerMove(cl_clientmovement_state_t *s) +static void CL_ClientMovement_PlayerMove(cl_clientmovement_state_t *s) { //Con_Printf(" %f", frametime); if (!s->cmd.jump) @@ -1438,6 +1490,7 @@ void CL_UpdateMoveVars(void) cl.movevars_maxairspeed = cl.statsf[STAT_MOVEVARS_MAXAIRSPEED]; cl.movevars_stepheight = cl.statsf[STAT_MOVEVARS_STEPHEIGHT]; cl.movevars_airaccel_qw = cl.statsf[STAT_MOVEVARS_AIRACCEL_QW]; + cl.movevars_airaccel_qw_stretchfactor = cl.statsf[STAT_MOVEVARS_AIRACCEL_QW_STRETCHFACTOR]; cl.movevars_airaccel_sideways_friction = cl.statsf[STAT_MOVEVARS_AIRACCEL_SIDEWAYS_FRICTION]; cl.movevars_friction = cl.statsf[STAT_MOVEVARS_FRICTION]; cl.movevars_wallfriction = cl.statsf[STAT_MOVEVARS_WALLFRICTION]; @@ -1445,18 +1498,22 @@ void CL_UpdateMoveVars(void) cl.movevars_airstopaccelerate = cl.statsf[STAT_MOVEVARS_AIRSTOPACCELERATE]; cl.movevars_airstrafeaccelerate = cl.statsf[STAT_MOVEVARS_AIRSTRAFEACCELERATE]; cl.movevars_maxairstrafespeed = cl.statsf[STAT_MOVEVARS_MAXAIRSTRAFESPEED]; + cl.movevars_airstrafeaccel_qw = cl.statsf[STAT_MOVEVARS_AIRSTRAFEACCEL_QW]; cl.movevars_aircontrol = cl.statsf[STAT_MOVEVARS_AIRCONTROL]; + cl.movevars_aircontrol_power = cl.statsf[STAT_MOVEVARS_AIRCONTROL_POWER]; + cl.movevars_aircontrol_penalty = cl.statsf[STAT_MOVEVARS_AIRCONTROL_PENALTY]; cl.movevars_warsowbunny_airforwardaccel = cl.statsf[STAT_MOVEVARS_WARSOWBUNNY_AIRFORWARDACCEL]; cl.movevars_warsowbunny_accel = cl.statsf[STAT_MOVEVARS_WARSOWBUNNY_ACCEL]; cl.movevars_warsowbunny_topspeed = cl.statsf[STAT_MOVEVARS_WARSOWBUNNY_TOPSPEED]; cl.movevars_warsowbunny_turnaccel = cl.statsf[STAT_MOVEVARS_WARSOWBUNNY_TURNACCEL]; cl.movevars_warsowbunny_backtosideratio = cl.statsf[STAT_MOVEVARS_WARSOWBUNNY_BACKTOSIDERATIO]; + cl.movevars_airspeedlimit_nonqw = cl.statsf[STAT_MOVEVARS_AIRSPEEDLIMIT_NONQW]; } else { cl.moveflags = 0; - cl.movevars_ticrate = slowmo.value / bound(1.0f, cl_netfps.value, 1000.0f); - cl.movevars_timescale = slowmo.value; + cl.movevars_ticrate = (cls.demoplayback ? 1.0f : slowmo.value) / bound(1.0f, cl_netfps.value, 1000.0f); + cl.movevars_timescale = (cls.demoplayback ? 1.0f : slowmo.value); cl.movevars_gravity = sv_gravity.value; cl.movevars_stopspeed = cl_movement_stopspeed.value; cl.movevars_maxspeed = cl_movement_maxspeed.value; @@ -1473,23 +1530,52 @@ void CL_UpdateMoveVars(void) cl.movevars_maxairspeed = cl_movement_maxairspeed.value; cl.movevars_stepheight = cl_movement_stepheight.value; cl.movevars_airaccel_qw = cl_movement_airaccel_qw.value; + cl.movevars_airaccel_qw_stretchfactor = 0; cl.movevars_airaccel_sideways_friction = cl_movement_airaccel_sideways_friction.value; cl.movevars_airstopaccelerate = 0; cl.movevars_airstrafeaccelerate = 0; cl.movevars_maxairstrafespeed = 0; + cl.movevars_airstrafeaccel_qw = 0; cl.movevars_aircontrol = 0; + cl.movevars_aircontrol_power = 2; + cl.movevars_aircontrol_penalty = 0; cl.movevars_warsowbunny_airforwardaccel = 0; cl.movevars_warsowbunny_accel = 0; cl.movevars_warsowbunny_topspeed = 0; cl.movevars_warsowbunny_turnaccel = 0; cl.movevars_warsowbunny_backtosideratio = 0; + cl.movevars_airspeedlimit_nonqw = 0; } if(!(cl.moveflags & MOVEFLAG_VALID)) { - if(gamemode == GAME_NEXUIZ) + if(gamemode == GAME_NEXUIZ) // Legacy hack to work with old servers of Nexuiz. cl.moveflags = MOVEFLAG_Q2AIRACCELERATE; } + + if(cl.movevars_aircontrol_power <= 0) + cl.movevars_aircontrol_power = 2; // CPMA default +} + +void CL_ClientMovement_PlayerMove_Frame(cl_clientmovement_state_t *s) +{ + // if a move is more than 50ms, do it as two moves (matching qwsv) + //Con_Printf("%i ", s.cmd.msec); + if(s->cmd.frametime > 0.0005) + { + if (s->cmd.frametime > 0.05) + { + s->cmd.frametime /= 2; + CL_ClientMovement_PlayerMove(s); + } + CL_ClientMovement_PlayerMove(s); + } + else + { + // we REALLY need this handling to happen, even if the move is not executed + if (!s->cmd.jump) + s->cmd.canjump = true; + } } void CL_ClientMovement_Replay(void) @@ -1498,9 +1584,14 @@ void CL_ClientMovement_Replay(void) double totalmovemsec; cl_clientmovement_state_t s; + VectorCopy(cl.mvelocity[0], cl.movement_velocity); + if (cl.movement_predicted && !cl.movement_replay) return; + if (!cl_movement_replay.integer) + return; + // set up starting state for the series of moves memset(&s, 0, sizeof(s)); VectorCopy(cl.entities[cl.playerentity].state_current.origin, s.origin); @@ -1513,7 +1604,7 @@ void CL_ClientMovement_Replay(void) if (cl.movecmd[i].sequence > cls.servermovesequence) totalmovemsec += cl.movecmd[i].msec; cl.movement_predicted = totalmovemsec >= cl_movement_minping.value && cls.servermovesequence && (cl_movement.integer && !cls.demoplayback && cls.signon == SIGNONS && cl.stats[STAT_HEALTH] > 0 && !cl.intermission); - //Con_Printf("%i = %.0f >= %.0f && %i && (%i && %i && %i == %i && %i > 0 && %i\n", cl.movement_predicted, totalmovemsec, cl_movement_minping.value, cls.servermovesequence, cl_movement.integer, !cls.demoplayback, cls.signon, SIGNONS, cl.stats[STAT_HEALTH], !cl.intermission); + //Con_Printf("%i = %.0f >= %.0f && %u && (%i && %i && %i == %i && %i > 0 && %i\n", cl.movement_predicted, totalmovemsec, cl_movement_minping.value, cls.servermovesequence, cl_movement.integer, !cls.demoplayback, cls.signon, SIGNONS, cl.stats[STAT_HEALTH], !cl.intermission); if (cl.movement_predicted) { //Con_Printf("%ims\n", cl.movecmd[0].msec); @@ -1531,18 +1622,10 @@ void CL_ClientMovement_Replay(void) s.cmd = cl.movecmd[i]; if (i < CL_MAX_USERCMDS - 1) s.cmd.canjump = cl.movecmd[i+1].canjump; - // if a move is more than 50ms, do it as two moves (matching qwsv) - //Con_Printf("%i ", s.cmd.msec); - if(s.cmd.frametime > 0.0005) - { - if (s.cmd.frametime > 0.05) - { - s.cmd.frametime /= 2; - CL_ClientMovement_PlayerMove(&s); - } - CL_ClientMovement_PlayerMove(&s); - cl.movecmd[i].canjump = s.cmd.canjump; - } + + CL_ClientMovement_PlayerMove_Frame(&s); + + cl.movecmd[i].canjump = s.cmd.canjump; } //Con_Printf("\n"); CL_ClientMovement_UpdateStatus(&s); @@ -1553,9 +1636,7 @@ void CL_ClientMovement_Replay(void) s.cmd = cl.movecmd[0]; } - if (cls.demoplayback) // for bob, speedometer - VectorCopy(cl.mvelocity[0], cl.movement_velocity); - else + if (!cls.demoplayback) // for bob, speedometer { cl.movement_replay = false; // update the interpolation target position and velocity @@ -1583,18 +1664,9 @@ void CL_ClientMovement_Replay(void) if (s.onground) cl.onground = true; } - - // react to onground state changes (for gun bob) - if (cl.onground) - { - if (!cl.oldonground) - cl.hitgroundtime = cl.movecmd[0].time; - cl.lastongroundtime = cl.movecmd[0].time; - } - cl.oldonground = cl.onground; } -void QW_MSG_WriteDeltaUsercmd(sizebuf_t *buf, usercmd_t *from, usercmd_t *to) +static void QW_MSG_WriteDeltaUsercmd(sizebuf_t *buf, usercmd_t *from, usercmd_t *to) { int bits; @@ -1645,6 +1717,25 @@ void CL_NewFrameReceived(int num) cl.latestframenumsposition = (cl.latestframenumsposition + 1) % LATESTFRAMENUMS; } +void CL_RotateMoves(const matrix4x4_t *m) +{ + // rotate viewangles in all previous moves + vec3_t v; + vec3_t f, r, u; + int i; + for (i = 0;i < CL_MAX_USERCMDS;i++) + { + if (cl.movecmd[i].sequence > cls.servermovesequence) + { + usercmd_t *c = &cl.movecmd[i]; + AngleVectors(c->viewangles, f, r, u); + Matrix4x4_Transform(m, f, v); VectorCopy(v, f); + Matrix4x4_Transform(m, u, v); VectorCopy(v, u); + AnglesFromVectors(c->viewangles, f, u, false); + } + } +} + /* ============== CL_SendMove @@ -1702,10 +1793,13 @@ void CL_SendMove(void) if (in_button16.state & 3) bits |= 262144; // button bits 19-31 unused currently // rotate/zoom view serverside if PRYDON_CLIENTCURSOR cursor is at edge of screen - if (cl.cmd.cursor_screen[0] <= -1) bits |= 8; - if (cl.cmd.cursor_screen[0] >= 1) bits |= 16; - if (cl.cmd.cursor_screen[1] <= -1) bits |= 32; - if (cl.cmd.cursor_screen[1] >= 1) bits |= 64; + if(cl_prydoncursor.integer > 0) + { + if (cl.cmd.cursor_screen[0] <= -1) bits |= 8; + if (cl.cmd.cursor_screen[0] >= 1) bits |= 16; + if (cl.cmd.cursor_screen[1] <= -1) bits |= 32; + if (cl.cmd.cursor_screen[1] >= 1) bits |= 64; + } // set buttons and impulse cl.cmd.buttons = bits; @@ -1721,7 +1815,20 @@ void CL_SendMove(void) cl.cmd.msec = 100; cl.cmd.frametime = cl.cmd.msec * (1.0 / 1000.0); - cl.cmd.predicted = cl_movement.integer != 0; + switch(cls.protocol) + { + case PROTOCOL_QUAKEWORLD: + // quakeworld uses a different cvar with opposite meaning, for compatibility + cl.cmd.predicted = cl_nopred.integer == 0; + break; + case PROTOCOL_DARKPLACES6: + case PROTOCOL_DARKPLACES7: + cl.cmd.predicted = cl_movement.integer != 0; + break; + default: + cl.cmd.predicted = false; + break; + } // movement is set by input code (forwardmove/sidemove/upmove) // always dump the first two moves, because they may contain leftover inputs from the last level @@ -1747,7 +1854,7 @@ void CL_SendMove(void) break; case PROTOCOL_DARKPLACES6: case PROTOCOL_DARKPLACES7: - // FIXME: cl.cmd.buttons & 16 is +button5, Nexuiz specific + // FIXME: cl.cmd.buttons & 16 is +button5, Nexuiz/Xonotic specific cl.cmd.crouch = (cl.cmd.buttons & 16) != 0; break; case PROTOCOL_UNKNOWN: @@ -1859,9 +1966,17 @@ void CL_SendMove(void) // 5 bytes MSG_WriteByte (&buf, clc_move); MSG_WriteFloat (&buf, cl.cmd.time); // last server packet time - // 3 bytes - for (i = 0;i < 3;i++) - MSG_WriteAngle8i (&buf, cl.cmd.viewangles[i]); + // 3 bytes (6 bytes in proquake) + if (cls.proquake_servermod == 1) // MOD_PROQUAKE + { + for (i = 0;i < 3;i++) + MSG_WriteAngle16i (&buf, cl.cmd.viewangles[i]); + } + else + { + for (i = 0;i < 3;i++) + MSG_WriteAngle8i (&buf, cl.cmd.viewangles[i]); + } // 6 bytes MSG_WriteCoord16i (&buf, cl.cmd.forwardmove); MSG_WriteCoord16i (&buf, cl.cmd.sidemove); @@ -1960,9 +2075,11 @@ void CL_SendMove(void) // if cl_netrepeatinput is 1 and client framerate matches server // framerate, this is 10 bytes, if client framerate is lower this // will be more... - int i, j; - int oldsequence = cl.cmd.sequence - bound(1, cl_netrepeatinput.integer + 1, 3); - if (oldsequence < 1) + unsigned int oldsequence = cl.cmd.sequence; + unsigned int delta = bound(1, cl_netrepeatinput.integer + 1, 3); + if (oldsequence > delta) + oldsequence = oldsequence - delta; + else oldsequence = 1; for (i = 0;i < LATESTFRAMENUMS;i++) { @@ -1992,7 +2109,7 @@ void CL_SendMove(void) // send the reliable message (forwarded commands) if there is one if (buf.cursize || cls.netcon->message.cursize) - NetConn_SendUnreliableMessage(cls.netcon, &buf, cls.protocol, max(20*(buf.cursize+40), cl_rate.integer), false); + NetConn_SendUnreliableMessage(cls.netcon, &buf, cls.protocol, max(20*(buf.cursize+40), cl_rate.integer), cl_rate_burstsize.integer, false); if (quemove) { @@ -2029,7 +2146,9 @@ void CL_SendMove(void) { Con_Print("CL_SendMove: lost server connection\n"); CL_Disconnect(); + SV_LockThreadMutex(); Host_ShutdownServer(); + SV_UnlockThreadMutex(); } } @@ -2117,6 +2236,7 @@ void CL_InitInput (void) Cvar_RegisterVariable(&cl_movecliptokeyboard); Cvar_RegisterVariable(&cl_movement); + Cvar_RegisterVariable(&cl_movement_replay); Cvar_RegisterVariable(&cl_movement_nettimeout); Cvar_RegisterVariable(&cl_movement_minping); Cvar_RegisterVariable(&cl_movement_track_canjump); @@ -2134,6 +2254,7 @@ void CL_InitInput (void) Cvar_RegisterVariable(&cl_movement_jumpvelocity); Cvar_RegisterVariable(&cl_movement_airaccel_qw); Cvar_RegisterVariable(&cl_movement_airaccel_sideways_friction); + Cvar_RegisterVariable(&cl_nopred); Cvar_RegisterVariable(&in_pitch_min); Cvar_RegisterVariable(&in_pitch_max); @@ -2148,5 +2269,7 @@ void CL_InitInput (void) Cvar_RegisterVariable(&cl_netimmediatebuttons); Cvar_RegisterVariable(&cl_nodelta); + + Cvar_RegisterVariable(&cl_csqc_generatemousemoveevents); }