]> de.git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - cl_input.c
significant cleanup of CL_UpdateEntities, GL_UpdateNetworkEntity, CL_LinkNetworkEntit...
[xonotic/darkplaces.git] / cl_input.c
index 1bdfd29e4869761cbdb8554d4e3e458075373542..358cb33d9e700fe297015bc509706d5e9d6d1705 100644 (file)
@@ -197,6 +197,57 @@ void IN_JumpUp (void) {KeyUp(&in_jump);}
 
 void IN_Impulse (void) {in_impulse=atoi(Cmd_Argv(1));}
 
+int in_bestweapon_info[][5] =
+{
+       {'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
+       {-1, 0, 0, 0, 0}
+};
+void IN_BestWeapon (void)
+{
+       int i, n;
+       const char *s;
+       if (Cmd_Argc() != 2)
+       {
+               Con_Printf("bestweapon requires 1 parameter\n");
+               return;
+       }
+       s = Cmd_Argv(1);
+       for (i = 0;s[i];i++)
+       {
+               // figure out which weapon this character refers to
+               for (n = 0;in_bestweapon_info[n][0] >= 0;n++)
+               {
+                       if (in_bestweapon_info[n][0] == s[i])
+                       {
+                               // 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][2]) && (cl.stats[in_bestweapon_info[n][3]] >= in_bestweapon_info[n][4]))
+                               {
+                                       // we found one of the weapons the player wanted
+                                       // send an impulse to switch to it
+                                       in_impulse = in_bestweapon_info[n][1];
+                                       return;
+                               }
+                               break;
+                       }
+               }
+               // if we couldn't identify the weapon we just ignore it and continue checking for other weapons
+       }
+       // if we couldn't find any of the weapons, there's nothing more we can do...
+}
+
 /*
 ===============
 CL_KeyState
@@ -281,6 +332,8 @@ cvar_t cl_movement_accelerate = {0, "cl_movement_accelerate", "10", "how fast yo
 cvar_t cl_movement_airaccelerate = {0, "cl_movement_airaccelerate", "-1", "how fast you accelerate while in the air (should match sv_airaccelerate), if less than 0 the cl_movement_accelerate variable is used instead"};
 cvar_t cl_movement_wateraccelerate = {0, "cl_movement_wateraccelerate", "-1", "how fast you accelerate while in the air (should match sv_airaccelerate), if less than 0 the cl_movement_accelerate variable is used instead"};
 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 (should match sv_airaccel_qw)"};
+cvar_t cl_movement_airaccel_sideways_friction = {0, "cl_movement_airaccel_sideways_friction", "0", "anti-sideways movement stabilization (should match sv_airaccel_sideways_friction)"};
 cvar_t cl_gravity = {0, "cl_gravity", "800", "how much gravity to apply in client physics (should match sv_gravity)"};
 cvar_t cl_slowmo = {0, "cl_slowmo", "1", "speed of game time (should match slowmo)"};
 
@@ -464,10 +517,9 @@ void CL_Move (void)
 
 #include "cl_collision.h"
 
-extern void V_CalcRefdef(void);
 void CL_UpdatePrydonCursor(void)
 {
-       vec3_t temp, scale;
+       vec3_t temp;
 
        if (!cl_prydoncursor.integer)
                VectorClear(cl.cmd.cursor_screen);
@@ -498,18 +550,10 @@ void CL_UpdatePrydonCursor(void)
        cl.cmd.cursor_screen[1] = bound(-1, cl.cmd.cursor_screen[1], 1);
        cl.cmd.cursor_screen[2] = 1;
 
-       scale[0] = -r_view.frustum_x;
-       scale[1] = -r_view.frustum_y;
-       scale[2] = 1;
-
-       // trace distance
-       VectorScale(scale, 1000000, scale);
-
        // calculate current view matrix
-       V_CalcRefdef();
-       VectorClear(temp);
-       Matrix4x4_Transform(&r_view.matrix, temp, cl.cmd.cursor_start);
-       VectorSet(temp, cl.cmd.cursor_screen[2] * scale[2], cl.cmd.cursor_screen[0] * scale[0], cl.cmd.cursor_screen[1] * scale[1]);
+       Matrix4x4_OriginFromMatrix(&r_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, 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);
        // 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, false);
@@ -639,6 +683,8 @@ typedef struct cl_clientmovement_state_s
        float movevars_edgefriction;
        float movevars_maxairspeed;
        float movevars_stepheight;
+       float movevars_airaccel_qw;
+       float movevars_airaccel_sideways_friction;
 
        // user command
        client_movementqueue_t q;
@@ -989,18 +1035,38 @@ void CL_ClientMovement_Physics_Walk(cl_clientmovement_state_t *s)
        {
                if (s->waterjumptime <= 0)
                {
+                       vec_t f;
+                       vec_t vel_straight;
+                       vec_t vel_z;
+                       vec3_t vel_perpend;
+
                        // apply air speed limit
                        wishspeed = min(wishspeed, s->movevars_maxairspeed);
-                       // Nexuiz has no upper bound on air acceleration, but little control
-                       if (gamemode == GAME_NEXUIZ)
-                               addspeed = wishspeed;
-                       else
-                               addspeed = wishspeed - DotProduct(s->velocity, wishdir);
+
+                       /*
+                       addspeed = wishspeed - DotProduct(s->velocity, wishdir);
                        if (addspeed > 0)
                        {
                                accelspeed = min(s->movevars_accelerate * s->q.frametime * wishspeed, addspeed);
                                VectorMA(s->velocity, accelspeed, wishdir, s->velocity);
                        }
+                       */
+
+                       vel_straight = DotProduct(s->velocity, wishdir);
+                       vel_z = s->velocity[2];
+                       VectorMA(s->velocity, -vel_straight, wishdir, vel_perpend);
+                       vel_perpend[2] -= vel_z;
+
+                       f = wishspeed - vel_straight;
+                       if(f > 0)
+                               vel_straight += min(f, s->movevars_accelerate * s->q.frametime * wishspeed) * s->movevars_airaccel_qw;
+                       if(wishspeed > 0)
+                               vel_straight += min(wishspeed, s->movevars_accelerate * s->q.frametime * wishspeed) * (1 - s->movevars_airaccel_qw);
+
+                       VectorM(1 - (s->q.frametime * (wishspeed / s->movevars_maxairspeed) * s->movevars_airaccel_sideways_friction), vel_perpend, vel_perpend);
+
+                       VectorMA(vel_perpend, vel_straight, wishdir, s->velocity);
+                       s->velocity[2] += vel_z;
                }
                s->velocity[2] -= cl_gravity.value * s->q.frametime;
                CL_ClientMovement_Move(s);
@@ -1035,6 +1101,7 @@ void CL_ClientMovement_Replay(void)
        VectorCopy(cl.mvelocity[0], s.velocity);
        s.crouched = true; // will be updated on first move
        s.canjump = cl.movement_replay_canjump;
+       //Con_Printf("movement replay starting org %f %f %f vel %f %f %f\n", s.origin[0], s.origin[1], s.origin[2], s.velocity[0], s.velocity[1], s.velocity[2]);
 
        // set up movement variables
        if (cls.protocol == PROTOCOL_QUAKEWORLD)
@@ -1053,6 +1120,8 @@ void CL_ClientMovement_Replay(void)
                s.movevars_edgefriction = cl_movement_edgefriction.value;
                s.movevars_maxairspeed = cl_movement_maxairspeed.value;
                s.movevars_stepheight = cl_movement_stepheight.value;
+               s.movevars_airaccel_qw = 1.0;
+               s.movevars_airaccel_sideways_friction = 0.0;
        }
        else
        {
@@ -1070,9 +1139,11 @@ void CL_ClientMovement_Replay(void)
                s.movevars_edgefriction = cl_movement_edgefriction.value;
                s.movevars_maxairspeed = cl_movement_maxairspeed.value;
                s.movevars_stepheight = cl_movement_stepheight.value;
+               s.movevars_airaccel_qw = cl_movement_airaccel_qw.value;
+               s.movevars_airaccel_sideways_friction = cl_movement_airaccel_sideways_friction.value;
        }
 
-       cl.movement_predicted = (cl_movement.integer && cls.signon == SIGNONS && cl.stats[STAT_HEALTH] > 0 && !cl.intermission) && ((cls.protocol != PROTOCOL_DARKPLACES6 && cls.protocol != PROTOCOL_DARKPLACES7) || cl.servermovesequence);
+       cl.movement_predicted = (cl_movement.integer && !cls.demoplayback && cls.signon == SIGNONS && cl.stats[STAT_HEALTH] > 0 && !cl.intermission) && ((cls.protocol != PROTOCOL_DARKPLACES6 && cls.protocol != PROTOCOL_DARKPLACES7) || cl.servermovesequence);
        if (cl.movement_predicted)
        {
                //Con_Printf("%f: ", cl.mtime[0]);
@@ -1097,9 +1168,9 @@ void CL_ClientMovement_Replay(void)
                // get the first movement queue entry to know whether to crouch and such
                s.q = cl.movement_queue[0];
        }
+
        // store replay location
        CL_ClientMovement_UpdateStatus(&s);
-       cl.onground = s.onground;
        cl.movement_time[1] = cl.movement_time[0];
        cl.movement_time[0] = cl.movement_queue[cl.movement_numqueue-1].time;
        VectorCopy(cl.movement_origin, cl.movement_oldorigin);
@@ -1107,6 +1178,27 @@ void CL_ClientMovement_Replay(void)
        VectorCopy(s.velocity, cl.movement_velocity);
        //VectorCopy(s.origin, cl.entities[cl.playerentity].state_current.origin);
        //VectorSet(cl.entities[cl.playerentity].state_current.angles, 0, cl.viewangles[1], 0);
+
+       // update the onground flag if appropriate
+       // when not predicted, cl.onground is only cleared by cl_parse.c, but can
+       // be set forcefully here to hide server inconsistencies in the onground
+       // flag (such as when stepping up stairs, the onground flag tends to turn
+       // off briefly due to precision errors, particularly at high framerates),
+       // such inconsistencies can mess up the gun bobbing and stair smoothing,
+       // so they must be avoided.
+       if (cl.movement_predicted)
+               cl.onground = s.onground;
+       else if (s.onground)
+               cl.onground = true;
+
+       // react to onground state changes (for gun bob)
+       if (cl.onground)
+       {
+               if (!cl.oldonground)
+                       cl.hitgroundtime = cl.time;
+               cl.lastongroundtime = cl.time;
+       }
+       cl.oldonground = cl.onground;
 }
 
 void QW_MSG_WriteDeltaUsercmd(sizebuf_t *buf, qw_usercmd_t *from, qw_usercmd_t *to)
@@ -1159,7 +1251,7 @@ CL_SendMove
 extern cvar_t cl_netinputpacketspersecond;
 void CL_SendMove(void)
 {
-       int i;
+       int i, j, packetloss;
        int bits;
        int impulse;
        sizebuf_t buf;
@@ -1191,11 +1283,13 @@ void CL_SendMove(void)
        }
        else
        {
-               if (realtime < lastsendtime + 1.0 / bound(10, cl_netinputpacketspersecond.value, 100))
+               double packettime = 1.0 / bound(10, cl_netinputpacketspersecond.value, 100);
+               // don't send too often or else network connections can get clogged by a high renderer framerate
+               if (realtime < lastsendtime + packettime)
                        return;
                // 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 = max(lastsendtime + 1.0 / bound(10, cl_netinputpacketspersecond.value, 100), realtime);
+               lastsendtime = max(lastsendtime + packettime, realtime);
        }
 #if MOVEAVERAGING
        // average the accumulated changes
@@ -1306,8 +1400,9 @@ void CL_SendMove(void)
                                checksumindex = buf.cursize;
                                MSG_WriteByte(&buf, 0);
                                // packet loss percentage
-                               // FIXME: netgraph stuff
-                               MSG_WriteByte(&buf, 0);
+                               for (j = 0, packetloss = 0;j < 100;j++)
+                                       packetloss += cls.netcon->packetlost[j];
+                               MSG_WriteByte(&buf, packetloss);
                                // write most recent 3 moves
                                i = (cls.netcon->qw.outgoing_sequence-2) & QW_UPDATE_MASK;
                                cmd = &cl.qw_moves[i];
@@ -1452,6 +1547,19 @@ void CL_SendMove(void)
                // PROTOCOL_DARKPLACES7 = 71 bytes per packet
        }
 
+       if (cls.protocol != PROTOCOL_QUAKEWORLD)
+       {
+               // acknowledge any recently received data blocks
+               for (i = 0;i < CL_MAX_DOWNLOADACKS && (cls.dp_downloadack[i].start || cls.dp_downloadack[i].size);i++)
+               {
+                       MSG_WriteByte(&buf, clc_ackdownloaddata);
+                       MSG_WriteLong(&buf, cls.dp_downloadack[i].start);
+                       MSG_WriteShort(&buf, cls.dp_downloadack[i].size);
+                       cls.dp_downloadack[i].start = 0;
+                       cls.dp_downloadack[i].size = 0;
+               }
+       }
+
        // send the reliable message (forwarded commands) if there is one
        NetConn_SendUnreliableMessage(cls.netcon, &buf, cls.protocol);
 
@@ -1538,6 +1646,9 @@ void CL_InitInput (void)
        Cmd_AddCommand ("+button16", IN_Button16Down, "activate button16 (behavior depends on mod)");
        Cmd_AddCommand ("-button16", IN_Button16Up, "deactivate button16");
 
+       // 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: 87654321)");
+
        Cvar_RegisterVariable(&cl_movement);
        Cvar_RegisterVariable(&cl_movement_latency);
        Cvar_RegisterVariable(&cl_movement_maxspeed);
@@ -1549,6 +1660,8 @@ void CL_InitInput (void)
        Cvar_RegisterVariable(&cl_movement_airaccelerate);
        Cvar_RegisterVariable(&cl_movement_accelerate);
        Cvar_RegisterVariable(&cl_movement_jumpvelocity);
+       Cvar_RegisterVariable(&cl_movement_airaccel_qw);
+       Cvar_RegisterVariable(&cl_movement_airaccel_sideways_friction);
        Cvar_RegisterVariable(&cl_gravity);
        Cvar_RegisterVariable(&cl_slowmo);