]> de.git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - cl_input.c
go back to block counting, looks like we now know what's broken with waveOut
[xonotic/darkplaces.git] / cl_input.c
index a7254701e5eca670604fcb4079f16f95ce5fb459..352e41330e2c9db390471cb5b8962a8267819dd6 100644 (file)
@@ -197,31 +197,76 @@ void IN_JumpUp (void) {KeyUp(&in_jump);}
 
 void IN_Impulse (void) {in_impulse=atoi(Cmd_Argv(1));}
 
-struct
+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)
+{
+       int i;
+       for(i = 0; i < IN_BESTWEAPON_MAX && in_bestweapon_info[i].impulse; ++i)
+               if(in_bestweapon_info[i].impulse == impulse)
+                       break;
+       if(i >= IN_BESTWEAPON_MAX)
+       {
+               Con_Printf("no slot left for weapon definition; increase IN_BESTWEAPON_MAX\n");
+               return; // sorry
+       }
+       strlcpy(in_bestweapon_info[i].name, name, sizeof(in_bestweapon_info[i].name));
+       in_bestweapon_info[i].impulse = impulse;
+       if(weaponbit != -1)
+               in_bestweapon_info[i].weaponbit = weaponbit;
+       if(activeweaponcode != -1)
+               in_bestweapon_info[i].activeweaponcode = activeweaponcode;
+       if(ammostat != -1)
+               in_bestweapon_info[i].ammostat = ammostat;
+       if(ammomin != -1)
+               in_bestweapon_info[i].ammomin = ammomin;
+}
+
+void IN_BestWeapon_ResetData (void)
 {
-       const char *name;
-       int impulse;
-       int weaponbit;
-       int ammostat;
-       int ammomin;
+       memset(in_bestweapon_info, 0, sizeof(in_bestweapon_info));
+       IN_BestWeapon_Register("1", 1, IT_AXE, IT_AXE, STAT_SHELLS, 0);
+       IN_BestWeapon_Register("2", 2, IT_SHOTGUN, IT_SHOTGUN, STAT_SHELLS, 1);
+       IN_BestWeapon_Register("3", 3, IT_SUPER_SHOTGUN, IT_SUPER_SHOTGUN, STAT_SHELLS, 1);
+       IN_BestWeapon_Register("4", 4, IT_NAILGUN, IT_NAILGUN, STAT_NAILS, 1);
+       IN_BestWeapon_Register("5", 5, IT_SUPER_NAILGUN, IT_SUPER_NAILGUN, STAT_NAILS, 1);
+       IN_BestWeapon_Register("6", 6, IT_GRENADE_LAUNCHER, IT_GRENADE_LAUNCHER, STAT_ROCKETS, 1);
+       IN_BestWeapon_Register("7", 7, IT_ROCKET_LAUNCHER, IT_ROCKET_LAUNCHER, STAT_ROCKETS, 1);
+       IN_BestWeapon_Register("8", 8, IT_LIGHTNING, IT_LIGHTNING, STAT_CELLS, 1);
+       IN_BestWeapon_Register("9", 9, 128, 128, STAT_CELLS, 1); // generic energy weapon for mods
+       IN_BestWeapon_Register("p", 209, 128, 128, STAT_CELLS, 1); // dpmod plasma gun
+       IN_BestWeapon_Register("w", 210, 8388608, 8388608, STAT_CELLS, 1); // dpmod plasma wave cannon
+       IN_BestWeapon_Register("l", 225, HIT_LASER_CANNON, HIT_LASER_CANNON, STAT_CELLS, 1); // hipnotic laser cannon
+       IN_BestWeapon_Register("h", 226, HIT_MJOLNIR, HIT_MJOLNIR, STAT_CELLS, 0); // hipnotic mjolnir hammer
 }
-in_bestweapon_info[] =
+
+void IN_BestWeapon_Register_f (void)
 {
-       {"1", 1, IT_AXE, STAT_SHELLS, 0},
-       {"2", 2, IT_SHOTGUN, STAT_SHELLS, 1},
-       {"3", 3, IT_SUPER_SHOTGUN, STAT_SHELLS, 1},
-       {"4", 4, IT_NAILGUN, STAT_NAILS, 1},
-       {"5", 5, IT_SUPER_NAILGUN, STAT_NAILS, 1},
-       {"6", 6, IT_GRENADE_LAUNCHER, STAT_ROCKETS, 1},
-       {"7", 7, IT_ROCKET_LAUNCHER, STAT_ROCKETS, 1},
-       {"8", 8, IT_LIGHTNING, STAT_CELLS, 1},
-       {"9", 9, 128, STAT_CELLS, 1}, // generic energy weapon for mods
-       {"p", 209, 128, STAT_CELLS, 1}, // dpmod plasma gun
-       {"w", 210, 8388608, STAT_CELLS, 1}, // dpmod plasma wave cannon
-       {"l", 225, HIT_LASER_CANNON, STAT_CELLS, 1}, // hipnotic laser cannon
-       {"h", 226, HIT_MJOLNIR, STAT_CELLS, 0}, // hipnotic mjolnir hammer
-       {NULL, 0, 0, 0, 0}
-};
+       if(Cmd_Argc() == 7)
+       {
+               IN_BestWeapon_Register(
+                       Cmd_Argv(1),
+                       atoi(Cmd_Argv(2)),
+                       atoi(Cmd_Argv(3)),
+                       atoi(Cmd_Argv(4)),
+                       atoi(Cmd_Argv(5)),
+                       atoi(Cmd_Argv(6))
+               );
+       }
+       else if(Cmd_Argc() == 2 && !strcmp(Cmd_Argv(1), "clear"))
+       {
+               memset(in_bestweapon_info, 0, sizeof(in_bestweapon_info));
+       }
+       else if(Cmd_Argc() == 2 && !strcmp(Cmd_Argv(1), "quake"))
+       {
+               IN_BestWeapon_ResetData();
+       }
+       else
+       {
+               Con_Printf("Usage: %s weaponshortname impulse itemcode activeweaponcode ammostat ammomin; %s clear; %s quake\n", Cmd_Argv(0), Cmd_Argv(0), Cmd_Argv(0));
+       }
+}
+
 void IN_BestWeapon (void)
 {
        int i, n;
@@ -235,7 +280,7 @@ void IN_BestWeapon (void)
        {
                t = Cmd_Argv(i);
                // figure out which weapon this character refers to
-               for (n = 0;in_bestweapon_info[n].name;n++)
+               for (n = 0;n < IN_BESTWEAPON_MAX && in_bestweapon_info[n].impulse;n++)
                {
                        if (!strcmp(in_bestweapon_info[n].name, t))
                        {
@@ -256,6 +301,53 @@ void IN_BestWeapon (void)
        // if we couldn't find any of the weapons, there's nothing more we can do...
 }
 
+void IN_CycleWeapon (void)
+{
+       int i, n;
+       int first = -1;
+       qboolean found = false;
+       const char *t;
+       if (Cmd_Argc() < 2)
+       {
+               Con_Printf("bestweapon requires 1 or more parameters\n");
+               return;
+       }
+       for (i = 1;i < Cmd_Argc();i++)
+       {
+               t = Cmd_Argv(i);
+               // figure out which weapon this character refers to
+               for (n = 0;n < IN_BESTWEAPON_MAX && in_bestweapon_info[n].impulse;n++)
+               {
+                       if (!strcmp(in_bestweapon_info[n].name, t))
+                       {
+                               // we found out what weapon this character refers to
+                               // check if the inventory contains the weapon and enough ammo
+                               if ((cl.stats[STAT_ITEMS] & in_bestweapon_info[n].weaponbit) && (cl.stats[in_bestweapon_info[n].ammostat] >= in_bestweapon_info[n].ammomin))
+                               {
+                                       // we found one of the weapons the player wanted
+                                       if(first == -1)
+                                               first = n;
+                                       if(found)
+                                       {
+                                               in_impulse = in_bestweapon_info[n].impulse;
+                                               return;
+                                       }
+                                       if(cl.stats[STAT_ACTIVEWEAPON] == in_bestweapon_info[n].activeweaponcode)
+                                               found = true;
+                               }
+                               break;
+                       }
+               }
+               // if we couldn't identify the weapon we just ignore it and continue checking for other weapons
+       }
+       if(first != -1)
+       {
+               in_impulse = in_bestweapon_info[first].impulse;
+               return;
+       }
+       // if we couldn't find any of the weapons, there's nothing more we can do...
+}
+
 /*
 ===============
 CL_KeyState
@@ -329,6 +421,7 @@ cvar_t cl_anglespeedkey = {CVAR_SAVE, "cl_anglespeedkey","1.5","how much +speed
 
 cvar_t cl_movement = {CVAR_SAVE, "cl_movement", "0", "enables clientside prediction of your player movement"};
 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"};
 cvar_t cl_movement_maxspeed = {0, "cl_movement_maxspeed", "320", "how fast you can move (should match sv_maxspeed)"};
 cvar_t cl_movement_maxairspeed = {0, "cl_movement_maxairspeed", "30", "how fast you can move while in the air (should match sv_maxairspeed)"};
 cvar_t cl_movement_stopspeed = {0, "cl_movement_stopspeed", "100", "speed below which you will be slowed rapidly to a stop rather than sliding endlessly (should match sv_stopspeed)"};
@@ -349,9 +442,9 @@ cvar_t in_pitch_max = {0, "in_pitch_max", "90", "how far upward you can aim (qua
 
 cvar_t m_filter = {CVAR_SAVE, "m_filter","0", "smoothes mouse movement, less responsive but smoother aiming"};
 
-cvar_t cl_netinputpacketsperserverpacket = {CVAR_SAVE, "cl_netinputpacketsperserverpacket", "1.0", "send this many input packets per server packet received"};
-cvar_t cl_netinputpacketspersecond = {CVAR_SAVE, "cl_netinputpacketspersecond","20", "how many input packets to send to server each second (only used on old servers, and note this is multiplied by cl_netinputpacketsperserverpacket)"};
-cvar_t cl_netinputpacketlosstolerance = {CVAR_SAVE, "cl_netinputpacketlosstolerance", "1", "how many packets in a row can be lost without movement issues when using cl_movement (technically how many input messages to repeat in each packet that have not yet been acknowledged by the server), only affects DP7 and later servers (Quake uses 0, QuakeWorld uses 2, and just for comparison Quake3 uses 1)"};
+cvar_t cl_netfps = {CVAR_SAVE, "cl_netfps","20", "how many input packets to send to server each second"};
+cvar_t cl_netrepeatinput = {CVAR_SAVE, "cl_netrepeatinput", "1", "how many packets in a row can be lost without movement issues when using cl_movement (technically how many input messages to repeat in each packet that have not yet been acknowledged by the server), only affects DP7 and later servers (Quake uses 0, QuakeWorld uses 2, and just for comparison Quake3 uses 1)"};
+cvar_t cl_netimmediatebuttons = {CVAR_SAVE, "cl_netimmediatebuttons", "1", "sends extra packets whenever your buttons change or an impulse is used (basically: whenever you click fire or change weapon)"};
 
 cvar_t cl_nodelta = {0, "cl_nodelta", "0", "disables delta compression of non-player entities in QW network protocol"};
 
@@ -396,20 +489,13 @@ void CL_AdjustAngles (void)
                V_StopPitchDrift ();
 
        cl.viewangles[YAW] = ANGLEMOD(cl.viewangles[YAW]);
-       cl.viewangles[PITCH] = ANGLEMOD(cl.viewangles[PITCH]);
-       cl.viewangles[ROLL] = ANGLEMOD(cl.viewangles[ROLL]);
        if (cl.viewangles[YAW] >= 180)
                cl.viewangles[YAW] -= 360;
-       if (cl.viewangles[PITCH] >= 180)
-               cl.viewangles[PITCH] -= 360;
-       if (cl.viewangles[ROLL] >= 180)
-               cl.viewangles[ROLL] -= 360;
-
-       cl.viewangles[PITCH] = bound (in_pitch_min.value, cl.viewangles[PITCH], in_pitch_max.value);
-       cl.viewangles[ROLL] = bound(-50, cl.viewangles[ROLL], 50);
+       cl.viewangles[PITCH] = bound(in_pitch_min.value, cl.viewangles[PITCH], in_pitch_max.value);
+       cl.viewangles[ROLL] = bound(-180, cl.viewangles[ROLL], 180);
 }
 
-qboolean cl_ignoremousemove = false;
+int cl_ignoremousemoves = 2;
 
 /*
 ================
@@ -468,9 +554,9 @@ void CL_Input (void)
        IN_Move ();
 
        // ignore a mouse move if mouse was activated/deactivated this frame
-       if (cl_ignoremousemove)
+       if (cl_ignoremousemoves)
        {
-               cl_ignoremousemove = false;
+               cl_ignoremousemoves--;
                in_mouse_x = 0;
                in_mouse_y = 0;
        }
@@ -489,38 +575,39 @@ void CL_Input (void)
        // if not in menu, apply mouse move to viewangles/movement
        if (!cl.csqc_wantsmousemove && in_client_mouse)
        {
+               float modulatedsensitivity = sensitivity.value * cl.sensitivityscale;
                if (cl_prydoncursor.integer)
                {
                        // mouse interacting with the scene, mostly stationary view
                        V_StopPitchDrift();
-                       cl.cmd.cursor_screen[0] += in_mouse_x * sensitivity.value / vid.width;
-                       cl.cmd.cursor_screen[1] += in_mouse_y * sensitivity.value / vid.height;
+                       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)
                {
                        // strafing mode, all looking is movement
                        V_StopPitchDrift();
-                       cl.cmd.sidemove += m_side.value * in_mouse_x * sensitivity.value;
+                       cl.cmd.sidemove += m_side.value * in_mouse_x * modulatedsensitivity;
                        if (noclip_anglehack)
-                               cl.cmd.upmove -= m_forward.value * in_mouse_y * sensitivity.value;
+                               cl.cmd.upmove -= m_forward.value * in_mouse_y * modulatedsensitivity;
                        else
-                               cl.cmd.forwardmove -= m_forward.value * in_mouse_y * sensitivity.value;
+                               cl.cmd.forwardmove -= m_forward.value * in_mouse_y * modulatedsensitivity;
                }
                else if ((in_mlook.state & 1) || freelook.integer)
                {
                        // mouselook, lookstrafe causes turning to become strafing
                        V_StopPitchDrift();
                        if (lookstrafe.integer)
-                               cl.cmd.sidemove += m_side.value * in_mouse_x * sensitivity.value;
+                               cl.cmd.sidemove += m_side.value * in_mouse_x * modulatedsensitivity;
                        else
-                               cl.viewangles[YAW] -= m_yaw.value * in_mouse_x * sensitivity.value * cl.viewzoom;
-                       cl.viewangles[PITCH] += m_pitch.value * in_mouse_y * sensitivity.value * cl.viewzoom;
+                               cl.viewangles[YAW] -= m_yaw.value * in_mouse_x * modulatedsensitivity * cl.viewzoom;
+                       cl.viewangles[PITCH] += m_pitch.value * in_mouse_y * modulatedsensitivity * cl.viewzoom;
                }
                else
                {
                        // non-mouselook, yaw turning and forward/back movement
-                       cl.viewangles[YAW] -= m_yaw.value * in_mouse_x * sensitivity.value * cl.viewzoom;
-                       cl.cmd.forwardmove -= m_forward.value * in_mouse_y * sensitivity.value;
+                       cl.viewangles[YAW] -= m_yaw.value * in_mouse_x * modulatedsensitivity * cl.viewzoom;
+                       cl.cmd.forwardmove -= m_forward.value * in_mouse_y * modulatedsensitivity;
                }
        }
 
@@ -570,10 +657,10 @@ void CL_UpdatePrydonCursor(void)
        cl.cmd.cursor_screen[2] = 1;
 
        // calculate current view matrix
-       Matrix4x4_OriginFromMatrix(&r_view.matrix, cl.cmd.cursor_start);
+       Matrix4x4_OriginFromMatrix(&r_refdef.view.matrix, cl.cmd.cursor_start);
        // calculate direction vector of cursor in viewspace by using frustum slopes
-       VectorSet(temp, cl.cmd.cursor_screen[2] * 1000000, (v_flipped.integer ? -1 : 1) * cl.cmd.cursor_screen[0] * -r_view.frustum_x * 1000000, cl.cmd.cursor_screen[1] * -r_view.frustum_y * 1000000);
-       Matrix4x4_Transform(&r_view.matrix, temp, cl.cmd.cursor_end);
+       VectorSet(temp, cl.cmd.cursor_screen[2] * 1000000, (v_flipped.integer ? -1 : 1) * cl.cmd.cursor_screen[0] * -r_refdef.view.frustum_x * 1000000, cl.cmd.cursor_screen[1] * -r_refdef.view.frustum_y * 1000000);
+       Matrix4x4_Transform(&r_refdef.view.matrix, temp, cl.cmd.cursor_end);
        // trace from view origin to the cursor
        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);
 }
@@ -937,7 +1024,7 @@ void CL_ClientMovement_Physics_Walk(cl_clientmovement_state_t *s)
        // released at least once since the last jump
        if (s->q.jump)
        {
-               if (s->onground && s->q.canjump)
+               if (s->onground && (s->q.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"
                {
                        s->velocity[2] += cl.movevars_jumpvelocity;
                        s->onground = false;
@@ -1059,10 +1146,10 @@ extern cvar_t slowmo;
 void CL_UpdateMoveVars(void)
 {
        if (cls.protocol == PROTOCOL_QUAKEWORLD)
-               cl.movevars_ticrate = 1.0 / bound(1, cl_netinputpacketspersecond.value, 100);
+       {
+       }
        else if (cl.stats[STAT_MOVEVARS_TICRATE])
        {
-               cl.movevars_ticrate = cl.statsf[STAT_MOVEVARS_TICRATE];
                cl.movevars_timescale = cl.statsf[STAT_MOVEVARS_TIMESCALE];
                cl.movevars_gravity = cl.statsf[STAT_MOVEVARS_GRAVITY];
                cl.movevars_stopspeed = cl.statsf[STAT_MOVEVARS_STOPSPEED] ;
@@ -1084,7 +1171,6 @@ void CL_UpdateMoveVars(void)
        }
        else
        {
-               cl.movevars_ticrate = 1.0 / bound(1, cl_netinputpacketspersecond.value, 100);
                cl.movevars_timescale = slowmo.value;
                cl.movevars_gravity = sv_gravity.value;
                cl.movevars_stopspeed = cl_movement_stopspeed.value;
@@ -1164,7 +1250,7 @@ void CL_ClientMovement_Replay(void)
                {
                        cl.movement_time[1] = cl.movement_time[0];
                        cl.movement_time[0] = cl.movecmd[0].time;
-                       cl.movement_time[2] = cl.time;
+                       cl.movement_time[2] = cl.movecmd[0].time;//time;
                        VectorCopy(cl.movement_origin, cl.movement_oldorigin);
                        //VectorCopy(s.origin, cl.entities[cl.playerentity].state_current.origin);
                        //VectorSet(cl.entities[cl.playerentity].state_current.angles, 0, cl.viewangles[1], 0);
@@ -1174,6 +1260,8 @@ void CL_ClientMovement_Replay(void)
                VectorCopy(s.origin, cl.movement_origin);
                VectorCopy(s.velocity, cl.movement_velocity);
        }
+       else if(cls.demoplayback) // for bob, speedometer
+               VectorCopy(cl.mvelocity[0], cl.movement_velocity);
 
        // update the onground flag if appropriate
        if (cl.movement_predicted)
@@ -1260,7 +1348,6 @@ void CL_SendMove(void)
        int bits;
        sizebuf_t buf;
        unsigned char data[1024];
-       static double lastsendtime = 0;
        double packettime;
        int msecdelta;
 
@@ -1270,43 +1357,52 @@ void CL_SendMove(void)
        if (!cls.netcon)
                return;
 
+       // set button bits
+       // LordHavoc: added 6 new buttons and use and chat buttons, and prydon cursor active button
+       bits = 0;
+       if (in_attack.state   & 3) bits |=   1;
+       if (in_jump.state     & 3) bits |=   2;
+       if (in_button3.state  & 3) bits |=   4;
+       if (in_button4.state  & 3) bits |=   8;
+       if (in_button5.state  & 3) bits |=  16;
+       if (in_button6.state  & 3) bits |=  32;
+       if (in_button7.state  & 3) bits |=  64;
+       if (in_button8.state  & 3) bits |= 128;
+       if (in_use.state      & 3) bits |= 256;
+       if (key_dest != key_game || key_consoleactive) bits |= 512;
+       if (cl_prydoncursor.integer) bits |= 1024;
+       if (in_button9.state  & 3)  bits |=   2048;
+       if (in_button10.state  & 3) bits |=   4096;
+       if (in_button11.state  & 3) bits |=   8192;
+       if (in_button12.state  & 3) bits |=  16384;
+       if (in_button13.state  & 3) bits |=  32768;
+       if (in_button14.state  & 3) bits |=  65536;
+       if (in_button15.state  & 3) bits |= 131072;
+       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;
+
        // don't send too often or else network connections can get clogged by a high renderer framerate
-       packettime = cl.movevars_ticrate / (double)bound(1, cl_netinputpacketsperserverpacket.value, 10);
+       packettime = 1.0 / bound(1, cl_netfps.value, 1000);
        // send input every frame in singleplayer
        if (cl.islocalgame)
                packettime = 0;
-       // quakeworld servers take only frametimes
-       // predicted dp7 servers take current interpolation time
-       // unpredicted servers take an echo of the latest server timestamp
-       if (cls.protocol == PROTOCOL_QUAKEWORLD)
-       {
-               if (realtime < lastsendtime + packettime)
-                       return;
-               cl.cmd.time = realtime;
-               cl.cmd.sequence = cls.netcon->qw.outgoing_sequence;
-       }
-       else if (cl.movement_predicted)
-       {
-               if (realtime < lastsendtime + packettime)
-                       return;
-               cl.cmd.time = cl.time;
-               cl.cmd.sequence = cls.movesequence;
-       }
+       // send the current interpolation time
+       cl.cmd.time = cl.time;
+       cl.cmd.sequence = cls.netcon->outgoing_unreliable_sequence;
+       if (realtime < cl.lastpackettime + packettime && (!cl_netimmediatebuttons.integer || (bits == cl.cmd.buttons && in_impulse == cl.cmd.impulse)))
+               return;
+       // try to round off the lastpackettime to a multiple of the packet interval
+       // (this causes it to emit packets at a steady beat, and takes advantage
+       //  of the time drift compensation in the cl.time code)
+       if (packettime > 0)
+               cl.lastpackettime = floor(realtime / packettime) * packettime;
        else
-       {
-               // unpredicted movement should be sent immediately whenever a server
-               // packet is received, to minimize ping times
-               if (!cl.movement_needupdate && realtime < lastsendtime + packettime)
-                       return;
-               if (cl.mtime[0] == cl.movecmd[0].time && cl.mtime[1] != cl.mtime[0])
-                       return;
-               cl.cmd.time = cl.mtime[0];
-               cl.cmd.sequence = cls.movesequence;
-       }
-
-       // don't let it fall behind if CL_SendMove hasn't been called recently
-       // (such is the case when framerate is too low for instance)
-       lastsendtime = bound(realtime, lastsendtime + packettime, realtime + packettime);
+               cl.lastpackettime = realtime;
        // set the flag indicating that we sent a packet recently
        cl.movement_needupdate = false;
 
@@ -1323,9 +1419,6 @@ void CL_SendMove(void)
        if ((cls.protocol == PROTOCOL_QUAKEWORLD || cls.signon == SIGNONS) && !NetConn_CanSend(cls.netcon) && !cl.islocalgame)
                return;
 
-       // increase the move counter since we intend to send a move
-       cls.movesequence++;
-
        // send the movement message
        // PROTOCOL_QUAKE        clc_move = 16 bytes total
        // PROTOCOL_QUAKEDP      clc_move = 16 bytes total
@@ -1342,38 +1435,30 @@ void CL_SendMove(void)
        // set prydon cursor info
        CL_UpdatePrydonCursor();
 
-       // set button bits
-       // LordHavoc: added 6 new buttons and use and chat buttons, and prydon cursor active button
-       bits = 0;
-       if (in_attack.state   & 3) bits |=   1;in_attack.state  &= ~2;
-       if (in_jump.state     & 3) bits |=   2;in_jump.state    &= ~2;
-       if (in_button3.state  & 3) bits |=   4;in_button3.state &= ~2;
-       if (in_button4.state  & 3) bits |=   8;in_button4.state &= ~2;
-       if (in_button5.state  & 3) bits |=  16;in_button5.state &= ~2;
-       if (in_button6.state  & 3) bits |=  32;in_button6.state &= ~2;
-       if (in_button7.state  & 3) bits |=  64;in_button7.state &= ~2;
-       if (in_button8.state  & 3) bits |= 128;in_button8.state &= ~2;
-       if (in_use.state      & 3) bits |= 256;in_use.state     &= ~2;
-       if (key_dest != key_game || key_consoleactive) bits |= 512;
-       if (cl_prydoncursor.integer) bits |= 1024;
-       if (in_button9.state  & 3)  bits |=   2048;in_button9.state  &= ~2;
-       if (in_button10.state  & 3) bits |=   4096;in_button10.state &= ~2;
-       if (in_button11.state  & 3) bits |=   8192;in_button11.state &= ~2;
-       if (in_button12.state  & 3) bits |=  16384;in_button12.state &= ~2;
-       if (in_button13.state  & 3) bits |=  32768;in_button13.state &= ~2;
-       if (in_button14.state  & 3) bits |=  65536;in_button14.state &= ~2;
-       if (in_button15.state  & 3) bits |= 131072;in_button15.state &= ~2;
-       if (in_button16.state  & 3) bits |= 262144;in_button16.state &= ~2;
-       // 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;
+       // set buttons and impulse
        cl.cmd.buttons = bits;
-
-       // set impulse
        cl.cmd.impulse = in_impulse;
+
+       // clear button 'click' states
+       in_attack.state  &= ~2;
+       in_jump.state    &= ~2;
+       in_button3.state &= ~2;
+       in_button4.state &= ~2;
+       in_button5.state &= ~2;
+       in_button6.state &= ~2;
+       in_button7.state &= ~2;
+       in_button8.state &= ~2;
+       in_use.state     &= ~2;
+       in_button9.state  &= ~2;
+       in_button10.state &= ~2;
+       in_button11.state &= ~2;
+       in_button12.state &= ~2;
+       in_button13.state &= ~2;
+       in_button14.state &= ~2;
+       in_button15.state &= ~2;
+       in_button16.state &= ~2;
+
+       // clear impulse
        in_impulse = 0;
 
        // movement is set by input code (forwardmove/sidemove/upmove)
@@ -1419,24 +1504,26 @@ void CL_SendMove(void)
                QW_MSG_WriteDeltaUsercmd(&buf, &cl.movecmd[2], &cl.movecmd[1]);
                QW_MSG_WriteDeltaUsercmd(&buf, &cl.movecmd[1], &cl.movecmd[0]);
                // calculate the checksum
-               buf.data[checksumindex] = COM_BlockSequenceCRCByteQW(buf.data + checksumindex + 1, buf.cursize - checksumindex - 1, cls.netcon->qw.outgoing_sequence);
+               buf.data[checksumindex] = COM_BlockSequenceCRCByteQW(buf.data + checksumindex + 1, buf.cursize - checksumindex - 1, cls.netcon->outgoing_unreliable_sequence);
                // if delta compression history overflows, request no delta
-               if (cls.netcon->qw.outgoing_sequence - cl.qw_validsequence >= QW_UPDATE_BACKUP-1)
+               if (cls.netcon->outgoing_unreliable_sequence - cl.qw_validsequence >= QW_UPDATE_BACKUP-1)
                        cl.qw_validsequence = 0;
                // request delta compression if appropriate
                if (cl.qw_validsequence && !cl_nodelta.integer && cls.state == ca_connected && !cls.demorecording)
                {
-                       cl.qw_deltasequence[cls.netcon->qw.outgoing_sequence & QW_UPDATE_MASK] = cl.qw_validsequence;
+                       cl.qw_deltasequence[cls.netcon->outgoing_unreliable_sequence & QW_UPDATE_MASK] = cl.qw_validsequence;
                        MSG_WriteByte(&buf, qw_clc_delta);
                        MSG_WriteByte(&buf, cl.qw_validsequence & 255);
                }
                else
-                       cl.qw_deltasequence[cls.netcon->qw.outgoing_sequence & QW_UPDATE_MASK] = -1;
+                       cl.qw_deltasequence[cls.netcon->outgoing_unreliable_sequence & QW_UPDATE_MASK] = -1;
        }
        else if (cls.signon == SIGNONS)
        {
-               if (cls.protocol == PROTOCOL_QUAKE || cls.protocol == PROTOCOL_QUAKEDP || cls.protocol == PROTOCOL_NEHAHRAMOVIE)
+               if (cls.protocol == PROTOCOL_QUAKE || cls.protocol == PROTOCOL_QUAKEDP || cls.protocol == PROTOCOL_NEHAHRAMOVIE || cls.protocol == PROTOCOL_NEHAHRABJP || cls.protocol == PROTOCOL_NEHAHRABJP2 || cls.protocol == PROTOCOL_NEHAHRABJP3)
                {
+                       CL_ClientMovement_Input((cl.movecmd[0].buttons & 2) != 0, false);
+
                        // 5 bytes
                        MSG_WriteByte (&buf, clc_move);
                        MSG_WriteFloat (&buf, cl.movecmd[0].time); // last server packet time
@@ -1453,6 +1540,8 @@ void CL_SendMove(void)
                }
                else if (cls.protocol == PROTOCOL_DARKPLACES2 || cls.protocol == PROTOCOL_DARKPLACES3)
                {
+                       CL_ClientMovement_Input((cl.movecmd[0].buttons & 2) != 0, false);
+
                        // 5 bytes
                        MSG_WriteByte (&buf, clc_move);
                        MSG_WriteFloat (&buf, cl.movecmd[0].time); // last server packet time
@@ -1469,6 +1558,8 @@ void CL_SendMove(void)
                }
                else if (cls.protocol == PROTOCOL_DARKPLACES1 || cls.protocol == PROTOCOL_DARKPLACES4 || cls.protocol == PROTOCOL_DARKPLACES5)
                {
+                       CL_ClientMovement_Input((cl.movecmd[0].buttons & 2) != 0, false);
+
                        // 5 bytes
                        MSG_WriteByte (&buf, clc_move);
                        MSG_WriteFloat (&buf, cl.movecmd[0].time); // last server packet time
@@ -1492,7 +1583,7 @@ void CL_SendMove(void)
                        CL_ClientMovement_Input((cl.movecmd[0].buttons & 2) != 0, (cl.movecmd[0].buttons & 16) != 0);
 
                        // set the maxusercmds variable to limit how many should be sent
-                       maxusercmds = bound(1, cl_netinputpacketlosstolerance.integer + 1, CL_MAX_USERCMDS);
+                       maxusercmds = bound(1, cl_netrepeatinput.integer + 1, CL_MAX_USERCMDS);
                        // when movement prediction is off, there's not much point in repeating old input as it will just be ignored
                        if (!cl.cmd.predicted)
                                maxusercmds = 1;
@@ -1656,9 +1747,12 @@ void CL_InitInput (void)
 
        // LordHavoc: added bestweapon command
        Cmd_AddCommand ("bestweapon", IN_BestWeapon, "send an impulse number to server to select the first usable weapon out of several (example: 8 7 6 5 4 3 2 1)");
+       Cmd_AddCommand ("cycleweapon", IN_CycleWeapon, "send an impulse number to server to select the next usable weapon out of several (example: 9 4 8) if you are holding one of these, and choose the first one if you are holding none of these");
+       Cmd_AddCommand ("register_bestweapon", IN_BestWeapon_Register_f, "(for QC usage only) change weapon parameters to be used by bestweapon; stuffcmd this in ClientConnect");
 
        Cvar_RegisterVariable(&cl_movement);
        Cvar_RegisterVariable(&cl_movement_minping);
+       Cvar_RegisterVariable(&cl_movement_track_canjump);
        Cvar_RegisterVariable(&cl_movement_maxspeed);
        Cvar_RegisterVariable(&cl_movement_maxairspeed);
        Cvar_RegisterVariable(&cl_movement_stopspeed);
@@ -1678,9 +1772,9 @@ void CL_InitInput (void)
        Cvar_RegisterVariable(&in_pitch_max);
        Cvar_RegisterVariable(&m_filter);
 
-       Cvar_RegisterVariable(&cl_netinputpacketsperserverpacket);
-       Cvar_RegisterVariable(&cl_netinputpacketspersecond);
-       Cvar_RegisterVariable(&cl_netinputpacketlosstolerance);
+       Cvar_RegisterVariable(&cl_netfps);
+       Cvar_RegisterVariable(&cl_netrepeatinput);
+       Cvar_RegisterVariable(&cl_netimmediatebuttons);
 
        Cvar_RegisterVariable(&cl_nodelta);
 }