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)
{
- const char *name;
- int impulse;
- int weaponbit;
- 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;
}
-in_bestweapon_info[] =
+
+void IN_BestWeapon_ResetData (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}
-};
+ 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
+}
+
+void IN_BestWeapon_Register_f (void)
+{
+ 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;
{
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))
{
// 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
cvar_t m_filter = {CVAR_SAVE, "m_filter","0", "smoothes mouse movement, less responsive but smoother aiming"};
-cvar_t cl_netinputpacketspersecond = {CVAR_SAVE, "cl_netinputpacketspersecond","50", "how many input packets to send to server each second"};
+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_netinputpacketspersecond_qw = {CVAR_SAVE, "cl_netinputpacketspersecond","72", "how many input packets to send to a qw server each second (only used on qw servers)"};
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_nodelta = {0, "cl_nodelta", "0", "disables delta compression of non-player entities in QW network protocol"};
+extern cvar_t v_flipped;
/*
================
// clamp before the move to prevent starting with bad angles
CL_AdjustAngles ();
+ if(v_flipped.integer)
+ cl.viewangles[YAW] = -cl.viewangles[YAW];
+
// reset some of the command fields
cl.cmd.forwardmove = 0;
cl.cmd.sidemove = 0;
}
}
+ if(v_flipped.integer)
+ {
+ cl.viewangles[YAW] = -cl.viewangles[YAW];
+ cl.cmd.sidemove = -cl.cmd.sidemove;
+ }
+
// clamp after the move to prevent rendering with bad angles
CL_AdjustAngles ();
}
// calculate current view matrix
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);
+ 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);
// 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);
void CL_UpdateMoveVars(void)
{
if (cls.protocol == PROTOCOL_QUAKEWORLD)
- cl.movevars_ticrate = 1.0 / bound(1, cl_netinputpacketspersecond.value, 100);
+ cl.movevars_ticrate = 1.0 / bound(1, cl_netinputpacketspersecond_qw.value, 100);
else if (cl.stats[STAT_MOVEVARS_TICRATE])
{
cl.movevars_ticrate = cl.statsf[STAT_MOVEVARS_TICRATE];
// don't send too often or else network connections can get clogged by a high renderer framerate
packettime = cl.movevars_ticrate;
+ if (cls.protocol != PROTOCOL_QUAKEWORLD)
+ packettime /= (double)bound(1, cl_netinputpacketsperserverpacket.value, 10);
+ // 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
+ cl.cmd.time = cl.time;
+ cl.cmd.sequence = cls.movesequence;
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;
- }
else
{
- // unpredicted movement should be sent immediately whenever a server
+ // 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
// don't send a new input packet if the connection is still saturated from
// the last one (or chat messages, etc)
// note: this behavior comes from QW
- if ((cls.protocol == PROTOCOL_QUAKEWORLD || cls.signon == SIGNONS) && !NetConn_CanSend(cls.netcon))
+ 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
}
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)
{
// 5 bytes
MSG_WriteByte (&buf, clc_move);
// 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));
+ NetConn_SendUnreliableMessage(cls.netcon, &buf, cls.protocol, max(20*(buf.cursize+40), cl_rate.integer), false);
if (cls.netcon->message.overflowed)
{
// 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(&in_pitch_max);
Cvar_RegisterVariable(&m_filter);
+ Cvar_RegisterVariable(&cl_netinputpacketsperserverpacket);
Cvar_RegisterVariable(&cl_netinputpacketspersecond);
+ Cvar_RegisterVariable(&cl_netinputpacketspersecond_qw);
Cvar_RegisterVariable(&cl_netinputpacketlosstolerance);
Cvar_RegisterVariable(&cl_nodelta);