Merge pull request #1 from rheit/master

Merge rheit: master into MajorCooke: master
This commit is contained in:
MajorCooke 2014-09-26 10:26:28 -05:00
commit c5db63fbbb
20 changed files with 567 additions and 65 deletions

View file

@ -756,9 +756,9 @@ void D_Display ()
} }
screen->SetBlendingRect(viewwindowx, viewwindowy, screen->SetBlendingRect(viewwindowx, viewwindowy,
viewwindowx + viewwidth, viewwindowy + viewheight); viewwindowx + viewwidth, viewwindowy + viewheight);
P_PredictPlayer(&players[consoleplayer]);
Renderer->RenderView(&players[consoleplayer]); Renderer->RenderView(&players[consoleplayer]);
P_UnPredictPlayer();
if ((hw2d = screen->Begin2D(viewactive))) if ((hw2d = screen->Begin2D(viewactive)))
{ {
// Redraw everything every frame when using 2D accel // Redraw everything every frame when using 2D accel

View file

@ -110,6 +110,8 @@ unsigned int lastrecvtime[MAXPLAYERS]; // [RH] Used for pings
unsigned int currrecvtime[MAXPLAYERS]; unsigned int currrecvtime[MAXPLAYERS];
unsigned int lastglobalrecvtime; // Identify the last time a packet was recieved. unsigned int lastglobalrecvtime; // Identify the last time a packet was recieved.
bool hadlate; bool hadlate;
int netdelay[MAXNETNODES][BACKUPTICS]; // Used for storing network delay times.
int lastaverage;
int nodeforplayer[MAXPLAYERS]; int nodeforplayer[MAXPLAYERS];
int playerfornode[MAXNETNODES]; int playerfornode[MAXNETNODES];
@ -151,6 +153,32 @@ CUSTOM_CVAR (Bool, cl_capfps, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
} }
} }
CVAR(Bool, net_ticbalance, false, CVAR_SERVERINFO | CVAR_NOSAVE)
CUSTOM_CVAR(Int, net_extratic, 0, CVAR_SERVERINFO | CVAR_NOSAVE)
{
if (self < 0)
{
self = 0;
}
else if (self > 2)
{
self = 2;
}
}
#ifdef _DEBUG
CVAR(Int, net_fakelatency, 0, 0);
struct PacketStore
{
int timer;
doomcom_t message;
};
static TArray<PacketStore> InBuffer;
static TArray<PacketStore> OutBuffer;
#endif
// [RH] Special "ticcmds" get stored in here // [RH] Special "ticcmds" get stored in here
static struct TicSpecial static struct TicSpecial
{ {
@ -347,6 +375,9 @@ int NetbufferSize ()
k += netbuffer[k] + 1; k += netbuffer[k] + 1;
} }
// Network delay byte
k++;
if (netbuffer[0] & NCMD_MULTI) if (netbuffer[0] & NCMD_MULTI)
{ {
count = netbuffer[k]; count = netbuffer[k];
@ -487,7 +518,30 @@ void HSendPacket (int node, int len)
doomcom.remotenode = node; doomcom.remotenode = node;
doomcom.datalength = len; doomcom.datalength = len;
I_NetCmd (); #ifdef _DEBUG
if (net_fakelatency / 2 > 0)
{
PacketStore store;
store.message = doomcom;
store.timer = I_GetTime(false) + ((net_fakelatency / 2) / (1000 / TICRATE));
OutBuffer.Push(store);
}
else
I_NetCmd();
for (unsigned int i = 0; i < OutBuffer.Size(); i++)
{
if (OutBuffer[i].timer <= I_GetTime(false))
{
doomcom = OutBuffer[i].message;
I_NetCmd();
OutBuffer.Delete(i);
i = -1;
}
}
#else
I_NetCmd();
#endif
} }
// //
@ -513,8 +567,38 @@ bool HGetPacket (void)
doomcom.command = CMD_GET; doomcom.command = CMD_GET;
I_NetCmd (); I_NetCmd ();
#ifdef _DEBUG
if (net_fakelatency / 2 > 0 && doomcom.remotenode != -1)
{
PacketStore store;
store.message = doomcom;
store.timer = I_GetTime(false) + ((net_fakelatency / 2) / (1000 / TICRATE));
InBuffer.Push(store);
doomcom.remotenode = -1;
}
if (doomcom.remotenode == -1) if (doomcom.remotenode == -1)
{
bool gotmessage = false;
for (unsigned int i = 0; i < InBuffer.Size(); i++)
{
if (InBuffer[i].timer <= I_GetTime(false))
{
doomcom = InBuffer[i].message;
InBuffer.Delete(i);
gotmessage = true;
break;
}
}
if (!gotmessage)
return false;
}
#else
if (doomcom.remotenode == -1)
{
return false; return false;
}
#endif
if (debugfile) if (debugfile)
{ {
@ -570,6 +654,9 @@ bool HGetPacket (void)
if (doomcom.datalength != NetbufferSize ()) if (doomcom.datalength != NetbufferSize ())
{ {
Printf("Bad packet length %i (calculated %i)\n",
doomcom.datalength, NetbufferSize());
if (debugfile) if (debugfile)
fprintf (debugfile,"---bad packet length %i (calculated %i)\n", fprintf (debugfile,"---bad packet length %i (calculated %i)\n",
doomcom.datalength, NetbufferSize()); doomcom.datalength, NetbufferSize());
@ -782,6 +869,9 @@ void GetPackets (void)
} }
} }
// Pull current network delay from node
netdelay[netnode][(nettics[netnode]+1) % BACKUPTICS] = netbuffer[k++];
playerbytes[0] = netconsole; playerbytes[0] = netconsole;
if (netbuffer[0] & NCMD_MULTI) if (netbuffer[0] & NCMD_MULTI)
{ {
@ -1150,11 +1240,18 @@ void NetUpdate (void)
netbuffer[k++] = lowtic; netbuffer[k++] = lowtic;
} }
numtics = lowtic - realstart; numtics = MAX(0, lowtic - realstart);
if (numtics > BACKUPTICS) if (numtics > BACKUPTICS)
I_Error ("NetUpdate: Node %d missed too many tics", i); I_Error ("NetUpdate: Node %d missed too many tics", i);
resendto[i] = MAX (0, lowtic - doomcom.extratics); switch (net_extratic)
{
case 0:
default:
resendto[i] = lowtic; break;
case 1: resendto[i] = MAX(0, lowtic - 1); break;
case 2: resendto[i] = nettics[i]; break;
}
if (numtics == 0 && resendOnly && !remoteresend[i] && nettics[i]) if (numtics == 0 && resendOnly && !remoteresend[i] && nettics[i])
{ {
@ -1190,6 +1287,10 @@ void NetUpdate (void)
} }
} }
// Send current network delay
// The number of tics we just made should be removed from the count.
netbuffer[k++] = ((maketic - newtics - gametic) / ticdup);
if (numtics > 0) if (numtics > 0)
{ {
int l; int l;
@ -1299,9 +1400,37 @@ void NetUpdate (void)
// that it won't adapt. Fortunately, player prediction helps // that it won't adapt. Fortunately, player prediction helps
// alleviate the lag somewhat. // alleviate the lag somewhat.
if (NetMode != NET_PacketServer) if (NetMode == NET_PeerToPeer)
{ {
mastertics = nettics[nodeforplayer[Net_Arbitrator]]; int totalavg = 0;
if (net_ticbalance)
{
// Try to guess ahead the time it takes to send responses to the slowest node
int nodeavg = 0, arbavg = 0;
for (j = 0; j < BACKUPTICS; j++)
{
arbavg += netdelay[nodeforplayer[Net_Arbitrator]][j];
nodeavg += netdelay[0][j];
}
arbavg /= BACKUPTICS;
nodeavg /= BACKUPTICS;
// We shouldn't adapt if we are already the arbitrator isn't what we are waiting for, otherwise it just adds more latency
if (arbavg > nodeavg)
{
lastaverage = totalavg = ((arbavg + nodeavg) / 2);
}
else
{
// Allow room to guess two tics ahead
if (nodeavg > (arbavg + 2) && lastaverage > 0)
lastaverage--;
totalavg = lastaverage;
}
}
mastertics = nettics[nodeforplayer[Net_Arbitrator]] + totalavg;
} }
if (nettics[0] <= mastertics) if (nettics[0] <= mastertics)
{ {
@ -1346,9 +1475,8 @@ void NetUpdate (void)
// //
// 0 One byte set to NCMD_SETUP+2 // 0 One byte set to NCMD_SETUP+2
// 1 One byte for ticdup setting // 1 One byte for ticdup setting
// 2 One byte for extratics setting // 2 One byte for NetMode setting
// 3 One byte for NetMode setting // 3 String with starting map's name
// 4 String with starting map's name
// . Four bytes for the RNG seed // . Four bytes for the RNG seed
// . Stream containing remaining game info // . Stream containing remaining game info
// //
@ -1429,10 +1557,9 @@ bool DoArbitrate (void *userdata)
data->gotsetup[0] = 0x80; data->gotsetup[0] = 0x80;
ticdup = doomcom.ticdup = netbuffer[1]; ticdup = doomcom.ticdup = netbuffer[1];
doomcom.extratics = netbuffer[2]; NetMode = netbuffer[2];
NetMode = netbuffer[3];
stream = &netbuffer[4]; stream = &netbuffer[3];
s = ReadString (&stream); s = ReadString (&stream);
startmap = s; startmap = s;
delete[] s; delete[] s;
@ -1497,9 +1624,8 @@ bool DoArbitrate (void *userdata)
{ {
netbuffer[0] = NCMD_SETUP+2; netbuffer[0] = NCMD_SETUP+2;
netbuffer[1] = (BYTE)doomcom.ticdup; netbuffer[1] = (BYTE)doomcom.ticdup;
netbuffer[2] = (BYTE)doomcom.extratics; netbuffer[2] = NetMode;
netbuffer[3] = NetMode; stream = &netbuffer[3];
stream = &netbuffer[4];
WriteString (startmap, &stream); WriteString (startmap, &stream);
WriteLong (rngseed, &stream); WriteLong (rngseed, &stream);
C_WriteCVars (&stream, CVAR_SERVERINFO, true); C_WriteCVars (&stream, CVAR_SERVERINFO, true);
@ -1647,15 +1773,23 @@ void D_CheckNetGame (void)
consoleplayer = doomcom.consoleplayer; consoleplayer = doomcom.consoleplayer;
v = Args->CheckValue ("-netmode"); if (consoleplayer == Net_Arbitrator)
if (v != NULL)
{ {
NetMode = atoi (v) != 0 ? NET_PacketServer : NET_PeerToPeer; v = Args->CheckValue("-netmode");
} if (v != NULL)
if (doomcom.numnodes > 1) {
{ NetMode = atoi(v) != 0 ? NET_PacketServer : NET_PeerToPeer;
Printf ("Selected " TEXTCOLOR_BLUE "%s" TEXTCOLOR_NORMAL " networking mode. (%s)\n", NetMode == NET_PeerToPeer ? "peer to peer" : "packet server", }
v != NULL ? "forced" : "auto"); if (doomcom.numnodes > 1)
{
Printf("Selected " TEXTCOLOR_BLUE "%s" TEXTCOLOR_NORMAL " networking mode. (%s)\n", NetMode == NET_PeerToPeer ? "peer to peer" : "packet server",
v != NULL ? "forced" : "auto");
}
if (Args->CheckParm("-extratic"))
{
net_extratic = 1;
}
} }
// [RH] Setup user info // [RH] Setup user info
@ -1683,6 +1817,11 @@ void D_CheckNetGame (void)
for (i = 0; i < doomcom.numnodes; i++) for (i = 0; i < doomcom.numnodes; i++)
nodeingame[i] = true; nodeingame[i] = true;
if (consoleplayer != Net_Arbitrator && doomcom.numnodes > 1)
{
Printf("Arbitrator selected " TEXTCOLOR_BLUE "%s" TEXTCOLOR_NORMAL " networking mode.\n", NetMode == NET_PeerToPeer ? "peer to peer" : "packet server");
}
Printf ("player %i of %i (%i nodes)\n", Printf ("player %i of %i (%i nodes)\n",
consoleplayer+1, doomcom.numplayers, doomcom.numnodes); consoleplayer+1, doomcom.numplayers, doomcom.numnodes);
} }
@ -1809,6 +1948,9 @@ void TryRunTics (void)
{ {
C_Ticker(); C_Ticker();
M_Ticker(); M_Ticker();
// Repredict the player for new buffered movement
P_UnPredictPlayer();
P_PredictPlayer(&players[consoleplayer]);
} }
return; return;
} }
@ -1844,6 +1986,9 @@ void TryRunTics (void)
{ {
C_Ticker (); C_Ticker ();
M_Ticker (); M_Ticker ();
// Repredict the player for new buffered movement
P_UnPredictPlayer();
P_PredictPlayer(&players[consoleplayer]);
return; return;
} }
} }
@ -1857,6 +2002,7 @@ void TryRunTics (void)
// run the count tics // run the count tics
if (counts > 0) if (counts > 0)
{ {
P_UnPredictPlayer();
while (counts--) while (counts--)
{ {
if (gametic > lowtic) if (gametic > lowtic)
@ -1876,6 +2022,7 @@ void TryRunTics (void)
NetUpdate (); // check for new console commands NetUpdate (); // check for new console commands
} }
P_PredictPlayer(&players[consoleplayer]);
S_UpdateSounds (players[consoleplayer].camera); // move positional sounds S_UpdateSounds (players[consoleplayer].camera); // move positional sounds
} }
} }
@ -2713,7 +2860,6 @@ void Net_SkipCommand (int type, BYTE **stream)
CCMD (pings) CCMD (pings)
{ {
int i; int i;
for (i = 0; i < MAXPLAYERS; i++) for (i = 0; i < MAXPLAYERS; i++)
if (playeringame[i]) if (playeringame[i])
Printf ("% 4d %s\n", currrecvtime[i] - lastrecvtime[i], Printf ("% 4d %s\n", currrecvtime[i] - lastrecvtime[i],

View file

@ -70,7 +70,6 @@ struct doomcom_t
// info common to all nodes // info common to all nodes
SWORD numnodes; // console is always node 0. SWORD numnodes; // console is always node 0.
SWORD ticdup; // 1 = no duplication, 2-5 = dup for slow nets SWORD ticdup; // 1 = no duplication, 2-5 = dup for slow nets
SWORD extratics; // 1 = send a backup tic in every packet
#ifdef DJGPP #ifdef DJGPP
SWORD pad[5]; // keep things aligned for DOS drivers SWORD pad[5]; // keep things aligned for DOS drivers
#endif #endif
@ -143,6 +142,8 @@ extern struct ticcmd_t localcmds[LOCALCMDTICS];
extern int maketic; extern int maketic;
extern int nettics[MAXNETNODES]; extern int nettics[MAXNETNODES];
extern int netdelay[MAXNETNODES][BACKUPTICS];
extern int nodeforplayer[MAXPLAYERS];
extern ticcmd_t netcmds[MAXPLAYERS][BACKUPTICS]; extern ticcmd_t netcmds[MAXPLAYERS][BACKUPTICS];
extern int ticdup; extern int ticdup;

View file

@ -37,6 +37,7 @@
// copy would be. // copy would be.
#include "doomtype.h" #include "doomtype.h"
#include "doomdef.h"
#include "v_video.h" #include "v_video.h"
#include "gi.h" #include "gi.h"
#include "c_cvars.h" #include "c_cvars.h"
@ -48,6 +49,7 @@
#include "p_local.h" #include "p_local.h"
#include "doomstat.h" #include "doomstat.h"
#include "g_level.h" #include "g_level.h"
#include "d_net.h"
#include <time.h> #include <time.h>
@ -73,6 +75,7 @@ CVAR (Bool, hud_showscore, false, CVAR_ARCHIVE); // for user maintained score
CVAR (Bool, hud_showweapons, true, CVAR_ARCHIVE); // Show weapons collected CVAR (Bool, hud_showweapons, true, CVAR_ARCHIVE); // Show weapons collected
CVAR (Int , hud_showtime, 0, CVAR_ARCHIVE); // Show time on HUD CVAR (Int , hud_showtime, 0, CVAR_ARCHIVE); // Show time on HUD
CVAR (Int , hud_timecolor, CR_GOLD,CVAR_ARCHIVE); // Color of in-game time on HUD CVAR (Int , hud_timecolor, CR_GOLD,CVAR_ARCHIVE); // Color of in-game time on HUD
CVAR (Int , hud_showlag, 0, CVAR_ARCHIVE); // Show input latency (maketic - gametic difference)
CVAR (Int, hud_ammo_red, 25, CVAR_ARCHIVE) // ammo percent less than which status is red CVAR (Int, hud_ammo_red, 25, CVAR_ARCHIVE) // ammo percent less than which status is red
CVAR (Int, hud_ammo_yellow, 50, CVAR_ARCHIVE) // ammo percent less is yellow more green CVAR (Int, hud_ammo_yellow, 50, CVAR_ARCHIVE) // ammo percent less is yellow more green
@ -917,6 +920,51 @@ static void DrawTime()
DrawHudText(SmallFont, hud_timecolor, timeString, hudwidth - width, height, FRACUNIT); DrawHudText(SmallFont, hud_timecolor, timeString, hudwidth - width, height, FRACUNIT);
} }
//---------------------------------------------------------------------------
//
// Draw in-game latency
//
//---------------------------------------------------------------------------
static void DrawLatency()
{
if (hud_showlag <= 0 ||
(hud_showlag == 1 && !netgame) ||
hud_showlag > 2)
{
return;
}
int i, localdelay = 0, arbitratordelay = 0;
for (i = 0; i < BACKUPTICS; i++) localdelay += netdelay[0][i];
for (i = 0; i < BACKUPTICS; i++) arbitratordelay += netdelay[nodeforplayer[Net_Arbitrator]][i];
localdelay = ((localdelay / BACKUPTICS) * ticdup) * (1000 / TICRATE);
arbitratordelay = ((arbitratordelay / BACKUPTICS) * ticdup) * (1000 / TICRATE);
int color = CR_GREEN;
if (MAX(localdelay, arbitratordelay) > 200)
{
color = CR_YELLOW;
}
if (MAX(localdelay, arbitratordelay) > 400)
{
color = CR_ORANGE;
}
if (MAX(localdelay, arbitratordelay) >= ((BACKUPTICS / 2 - 1) * ticdup) * (1000 / TICRATE))
{
color = CR_RED;
}
char tempstr[32];
const int millis = (level.time % TICRATE) * (1000 / TICRATE);
mysnprintf(tempstr, sizeof(tempstr), "a:%dms - l:%dms", arbitratordelay, localdelay);
const int characterCount = strlen(tempstr);
const int width = SmallFont->GetCharWidth('0') * characterCount + 2; // small offset from screen's border
const int height = SmallFont->GetHeight() * 2;
DrawHudText(SmallFont, color, tempstr, hudwidth - width, height, FRACUNIT);
}
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
// //
@ -982,6 +1030,7 @@ void DrawHUD()
if (idmypos) DrawCoordinates(CPlayer); if (idmypos) DrawCoordinates(CPlayer);
DrawTime(); DrawTime();
DrawLatency();
} }
else else
{ {

View file

@ -48,6 +48,7 @@
#include "d_player.h" #include "d_player.h"
#include "hu_stuff.h" #include "hu_stuff.h"
#include "gstrings.h" #include "gstrings.h"
#include "d_net.h"
// MACROS ------------------------------------------------------------------ // MACROS ------------------------------------------------------------------
@ -61,7 +62,7 @@
static void HU_DoDrawScores (player_t *, player_t *[MAXPLAYERS]); static void HU_DoDrawScores (player_t *, player_t *[MAXPLAYERS]);
static void HU_DrawTimeRemaining (int y); static void HU_DrawTimeRemaining (int y);
static void HU_DrawPlayer (player_t *, bool, int, int, int, int, int, int, int, int); static void HU_DrawPlayer(player_t *, bool, int, int, int, int, int, int, int, int, int);
// EXTERNAL DATA DECLARATIONS ---------------------------------------------- // EXTERNAL DATA DECLARATIONS ----------------------------------------------
@ -228,7 +229,7 @@ static void HU_DoDrawScores (player_t *player, player_t *sortedplayers[MAXPLAYER
int maxnamewidth, maxscorewidth, maxiconheight; int maxnamewidth, maxscorewidth, maxiconheight;
int numTeams = 0; int numTeams = 0;
int x, y, ypadding, bottom; int x, y, ypadding, bottom;
int col2, col3, col4; int col2, col3, col4, col5;
if (deathmatch) if (deathmatch)
{ {
@ -309,12 +310,14 @@ static void HU_DoDrawScores (player_t *player, player_t *sortedplayers[MAXPLAYER
const char *text_color = GStrings("SCORE_COLOR"), const char *text_color = GStrings("SCORE_COLOR"),
*text_frags = GStrings(deathmatch ? "SCORE_FRAGS" : "SCORE_KILLS"), *text_frags = GStrings(deathmatch ? "SCORE_FRAGS" : "SCORE_KILLS"),
*text_name = GStrings("SCORE_NAME"); *text_name = GStrings("SCORE_NAME"),
*text_delay = GStrings("SCORE_DELAY");
col2 = (SmallFont->StringWidth(text_color) + 8) * CleanXfac; col2 = (SmallFont->StringWidth(text_color) + 8) * CleanXfac;
col3 = col2 + (SmallFont->StringWidth(text_frags) + 8) * CleanXfac; col3 = col2 + (SmallFont->StringWidth(text_frags) + 8) * CleanXfac;
col4 = col3 + maxscorewidth * CleanXfac; col4 = col3 + maxscorewidth * CleanXfac;
x = (SCREENWIDTH >> 1) - ((maxnamewidth * CleanXfac + col4) >> 1); col5 = col4 + (maxnamewidth + 8) * CleanXfac;
x = (SCREENWIDTH >> 1) - (((SmallFont->StringWidth(text_delay) * CleanXfac) + col5) >> 1);
screen->DrawText (SmallFont, color, x, y, text_color, screen->DrawText (SmallFont, color, x, y, text_color,
DTA_CleanNoMove, true, TAG_DONE); DTA_CleanNoMove, true, TAG_DONE);
@ -325,6 +328,9 @@ static void HU_DoDrawScores (player_t *player, player_t *sortedplayers[MAXPLAYER
screen->DrawText (SmallFont, color, x + col4, y, text_name, screen->DrawText (SmallFont, color, x + col4, y, text_name,
DTA_CleanNoMove, true, TAG_DONE); DTA_CleanNoMove, true, TAG_DONE);
screen->DrawText(SmallFont, color, x + col5, y, text_delay,
DTA_CleanNoMove, true, TAG_DONE);
y += height + 6 * CleanYfac; y += height + 6 * CleanYfac;
bottom -= height; bottom -= height;
@ -332,7 +338,7 @@ static void HU_DoDrawScores (player_t *player, player_t *sortedplayers[MAXPLAYER
{ {
if (playeringame[sortedplayers[i] - players]) if (playeringame[sortedplayers[i] - players])
{ {
HU_DrawPlayer (sortedplayers[i], player==sortedplayers[i], x, col2, col3, col4, maxnamewidth, y, ypadding, lineheight); HU_DrawPlayer(sortedplayers[i], player == sortedplayers[i], x, col2, col3, col4, col5, maxnamewidth, y, ypadding, lineheight);
y += lineheight + CleanYfac; y += lineheight + CleanYfac;
} }
} }
@ -377,7 +383,7 @@ static void HU_DrawTimeRemaining (int y)
// //
//========================================================================== //==========================================================================
static void HU_DrawPlayer (player_t *player, bool highlight, int col1, int col2, int col3, int col4, int maxnamewidth, int y, int ypadding, int height) static void HU_DrawPlayer (player_t *player, bool highlight, int col1, int col2, int col3, int col4, int col5, int maxnamewidth, int y, int ypadding, int height)
{ {
int color; int color;
char str[80]; char str[80];
@ -387,12 +393,13 @@ static void HU_DrawPlayer (player_t *player, bool highlight, int col1, int col2,
// The teamplay mode uses colors to show teams, so we need some // The teamplay mode uses colors to show teams, so we need some
// other way to do highlighting. And it may as well be used for // other way to do highlighting. And it may as well be used for
// all modes for the sake of consistancy. // all modes for the sake of consistancy.
screen->Dim(MAKERGB(200,245,255), 0.125f, col1 - 12*CleanXfac, y - 1, col4 + (maxnamewidth + 24)*CleanXfac, height + 2); screen->Dim(MAKERGB(200,245,255), 0.125f, col1 - 12*CleanXfac, y - 1, col5 + (maxnamewidth + 24)*CleanXfac, height + 2);
} }
col2 += col1; col2 += col1;
col3 += col1; col3 += col1;
col4 += col1; col4 += col1;
col5 += col1;
color = HU_GetRowColor(player, highlight); color = HU_GetRowColor(player, highlight);
HU_DrawColorBar(col1, y, height, (int)(player - players)); HU_DrawColorBar(col1, y, height, (int)(player - players));
@ -412,6 +419,18 @@ static void HU_DrawPlayer (player_t *player, bool highlight, int col1, int col2,
screen->DrawText (SmallFont, color, col4, y + ypadding, player->userinfo.GetName(), screen->DrawText (SmallFont, color, col4, y + ypadding, player->userinfo.GetName(),
DTA_CleanNoMove, true, TAG_DONE); DTA_CleanNoMove, true, TAG_DONE);
int avgdelay = 0;
for (int i = 0; i < BACKUPTICS; i++)
{
avgdelay += netdelay[nodeforplayer[(int)(player - players)]][i];
}
avgdelay /= BACKUPTICS;
mysnprintf(str, countof(str), "%d", (avgdelay * ticdup) * (1000 / TICRATE));
screen->DrawText(SmallFont, color, col5, y + ypadding, str,
DTA_CleanNoMove, true, TAG_DONE);
if (teamplay && Teams[player->userinfo.GetTeam()].GetLogo().IsNotEmpty ()) if (teamplay && Teams[player->userinfo.GetTeam()].GetLogo().IsNotEmpty ())
{ {
FTexture *pic = TexMan[Teams[player->userinfo.GetTeam()].GetLogo().GetChars ()]; FTexture *pic = TexMan[Teams[player->userinfo.GetTeam()].GetLogo().GetChars ()];

View file

@ -208,11 +208,11 @@ void PacketSend (void)
{ {
I_FatalError("Netbuffer overflow!"); I_FatalError("Netbuffer overflow!");
} }
assert(!(doomcom.data[0] & NCMD_COMPRESSED));
uLong size = TRANSMIT_SIZE - 1; uLong size = TRANSMIT_SIZE - 1;
if (doomcom.datalength >= 10) if (doomcom.datalength >= 10)
{ {
assert(!(doomcom.data[0] & NCMD_COMPRESSED));
TransmitBuffer[0] = doomcom.data[0] | NCMD_COMPRESSED; TransmitBuffer[0] = doomcom.data[0] | NCMD_COMPRESSED;
c = compress2(TransmitBuffer + 1, &size, doomcom.data + 1, doomcom.datalength - 1, 9); c = compress2(TransmitBuffer + 1, &size, doomcom.data + 1, doomcom.datalength - 1, 9);
size += 1; size += 1;
@ -938,11 +938,6 @@ bool I_InitNetwork (void)
doomcom.ticdup = 1; doomcom.ticdup = 1;
} }
if (Args->CheckParm ("-extratic"))
doomcom.extratics = 1;
else
doomcom.extratics = 0;
v = Args->CheckValue ("-port"); v = Args->CheckValue ("-port");
if (v) if (v)
{ {

View file

@ -298,6 +298,7 @@ xx(Abs)
xx(ACS_NamedExecuteWithResult) xx(ACS_NamedExecuteWithResult)
xx(CallACS) xx(CallACS)
xx(Sqrt) xx(Sqrt)
xx(CheckClass)
// Various actor names which are used internally // Various actor names which are used internally
xx(MapSpot) xx(MapSpot)

View file

@ -380,7 +380,9 @@ bool P_TeleportMove(AActor *thing, fixed_t x, fixed_t y, fixed_t z, bool telefra
// ... and some items can never be telefragged while others will be telefragged by everything that teleports upon them. // ... and some items can never be telefragged while others will be telefragged by everything that teleports upon them.
if ((StompAlwaysFrags && !(th->flags6 & MF6_NOTELEFRAG)) || (th->flags7 & MF7_ALWAYSTELEFRAG)) if ((StompAlwaysFrags && !(th->flags6 & MF6_NOTELEFRAG)) || (th->flags7 & MF7_ALWAYSTELEFRAG))
{ {
P_DamageMobj(th, thing, thing, TELEFRAG_DAMAGE, NAME_Telefrag, DMG_THRUSTLESS); // Don't actually damage if predicting a teleport
if (thing->player == NULL || !(thing->player->cheats & CF_PREDICTING))
P_DamageMobj(th, thing, thing, TELEFRAG_DAMAGE, NAME_Telefrag, DMG_THRUSTLESS);
continue; continue;
} }
return false; return false;
@ -1981,13 +1983,6 @@ bool P_TryMove(AActor *thing, fixed_t x, fixed_t y,
thing->AdjustFloorClip(); thing->AdjustFloorClip();
} }
// [RH] Don't activate anything if just predicting
if (thing->player && (thing->player->cheats & CF_PREDICTING))
{
thing->flags6 &= ~MF6_INTRYMOVE;
return true;
}
// if any special lines were hit, do the effect // if any special lines were hit, do the effect
if (!(thing->flags & (MF_TELEPORT | MF_NOCLIP))) if (!(thing->flags & (MF_TELEPORT | MF_NOCLIP)))
{ {
@ -1998,7 +1993,11 @@ bool P_TryMove(AActor *thing, fixed_t x, fixed_t y,
oldside = P_PointOnLineSide(oldx, oldy, ld); oldside = P_PointOnLineSide(oldx, oldy, ld);
if (side != oldside && ld->special && !(thing->flags6 & MF6_NOTRIGGER)) if (side != oldside && ld->special && !(thing->flags6 & MF6_NOTRIGGER))
{ {
if (thing->player) if (thing->player && (thing->player->cheats & CF_PREDICTING))
{
P_PredictLine(ld, thing, oldside, SPAC_Cross);
}
else if (thing->player)
{ {
P_ActivateLine(ld, thing, oldside, SPAC_Cross); P_ActivateLine(ld, thing, oldside, SPAC_Cross);
} }
@ -2024,6 +2023,13 @@ bool P_TryMove(AActor *thing, fixed_t x, fixed_t y,
} }
} }
// [RH] Don't activate anything if just predicting
if (thing->player && (thing->player->cheats & CF_PREDICTING))
{
thing->flags6 &= ~MF6_INTRYMOVE;
return true;
}
// [RH] Check for crossing fake floor/ceiling // [RH] Check for crossing fake floor/ceiling
newsec = thing->Sector; newsec = thing->Sector;
if (newsec->heightsec && oldsec->heightsec && newsec->SecActTarget) if (newsec->heightsec && oldsec->heightsec && newsec->SecActTarget)

View file

@ -73,6 +73,7 @@
static FRandom pr_playerinspecialsector ("PlayerInSpecialSector"); static FRandom pr_playerinspecialsector ("PlayerInSpecialSector");
void P_SetupPortals(); void P_SetupPortals();
EXTERN_CVAR(Bool, cl_predict_specials)
IMPLEMENT_POINTY_CLASS (DScroller) IMPLEMENT_POINTY_CLASS (DScroller)
DECLARE_POINTER (m_Interpolations[0]) DECLARE_POINTER (m_Interpolations[0])
@ -408,6 +409,48 @@ bool P_TestActivateLine (line_t *line, AActor *mo, int side, int activationType)
return true; return true;
} }
//============================================================================
//
// P_PredictLine
//
//============================================================================
bool P_PredictLine(line_t *line, AActor *mo, int side, int activationType)
{
int lineActivation;
INTBOOL buttonSuccess;
BYTE special;
// Only predict a very specifc section of specials
if (line->special != Teleport_Line &&
line->special != Teleport)
{
return false;
}
if (!P_TestActivateLine(line, mo, side, activationType) || !cl_predict_specials)
{
return false;
}
if (line->locknumber > 0) return false;
lineActivation = line->activation;
buttonSuccess = false;
buttonSuccess = P_ExecuteSpecial(line->special,
line, mo, side == 1, line->args[0],
line->args[1], line->args[2],
line->args[3], line->args[4]);
special = line->special;
// end of changed code
if (developer && buttonSuccess)
{
Printf("Line special %d predicted on line %i\n", special, int(line - lines));
}
return true;
}
// //
// P_PlayerInSpecialSector // P_PlayerInSpecialSector
// Called every tic frame // Called every tic frame

View file

@ -166,6 +166,7 @@ void P_UpdateSpecials (void);
// when needed // when needed
bool P_ActivateLine (line_t *ld, AActor *mo, int side, int activationType); bool P_ActivateLine (line_t *ld, AActor *mo, int side, int activationType);
bool P_TestActivateLine (line_t *ld, AActor *mo, int side, int activationType); bool P_TestActivateLine (line_t *ld, AActor *mo, int side, int activationType);
bool P_PredictLine (line_t *ld, AActor *mo, int side, int activationType);
void P_PlayerInSpecialSector (player_t *player, sector_t * sector=NULL); void P_PlayerInSpecialSector (player_t *player, sector_t * sector=NULL);
void P_PlayerOnSpecialFlat (player_t *player, int floorType); void P_PlayerOnSpecialFlat (player_t *player, int floorType);

View file

@ -96,6 +96,8 @@ void P_SpawnTeleportFog(fixed_t x, fixed_t y, fixed_t z, int spawnid)
bool P_Teleport (AActor *thing, fixed_t x, fixed_t y, fixed_t z, angle_t angle, bool P_Teleport (AActor *thing, fixed_t x, fixed_t y, fixed_t z, angle_t angle,
bool useFog, bool sourceFog, bool keepOrientation, bool bHaltVelocity, bool keepHeight) bool useFog, bool sourceFog, bool keepOrientation, bool bHaltVelocity, bool keepHeight)
{ {
bool predicting = (thing->player && (thing->player->cheats & CF_PREDICTING));
fixed_t oldx; fixed_t oldx;
fixed_t oldy; fixed_t oldy;
fixed_t oldz; fixed_t oldz;
@ -181,7 +183,7 @@ bool P_Teleport (AActor *thing, fixed_t x, fixed_t y, fixed_t z, angle_t angle,
angle = thing->angle; angle = thing->angle;
} }
// Spawn teleport fog at source and destination // Spawn teleport fog at source and destination
if (sourceFog) if (sourceFog && !predicting)
{ {
fixed_t fogDelta = thing->flags & MF_MISSILE ? 0 : TELEFOGHEIGHT; fixed_t fogDelta = thing->flags & MF_MISSILE ? 0 : TELEFOGHEIGHT;
AActor *fog = Spawn<ATeleportFog> (oldx, oldy, oldz + fogDelta, ALLOW_REPLACE); AActor *fog = Spawn<ATeleportFog> (oldx, oldy, oldz + fogDelta, ALLOW_REPLACE);
@ -189,11 +191,14 @@ bool P_Teleport (AActor *thing, fixed_t x, fixed_t y, fixed_t z, angle_t angle,
} }
if (useFog) if (useFog)
{ {
fixed_t fogDelta = thing->flags & MF_MISSILE ? 0 : TELEFOGHEIGHT; if (!predicting)
an = angle >> ANGLETOFINESHIFT; {
AActor *fog = Spawn<ATeleportFog> (x + 20*finecosine[an], fixed_t fogDelta = thing->flags & MF_MISSILE ? 0 : TELEFOGHEIGHT;
y + 20*finesine[an], thing->z + fogDelta, ALLOW_REPLACE); an = angle >> ANGLETOFINESHIFT;
fog->target = thing; AActor *fog = Spawn<ATeleportFog>(x + 20 * finecosine[an],
y + 20 * finesine[an], thing->z + fogDelta, ALLOW_REPLACE);
fog->target = thing;
}
if (thing->player) if (thing->player)
{ {
// [RH] Zoom player's field of vision // [RH] Zoom player's field of vision
@ -226,7 +231,7 @@ bool P_Teleport (AActor *thing, fixed_t x, fixed_t y, fixed_t z, angle_t angle,
return true; return true;
} }
static AActor *SelectTeleDest (int tid, int tag) static AActor *SelectTeleDest (int tid, int tag, bool norandom)
{ {
AActor *searcher; AActor *searcher;
@ -276,7 +281,7 @@ static AActor *SelectTeleDest (int tid, int tag)
} }
else else
{ {
if (count != 1) if (count != 1 && !norandom)
{ {
count = 1 + (pr_teleport() % count); count = 1 + (pr_teleport() % count);
} }
@ -323,6 +328,8 @@ static AActor *SelectTeleDest (int tid, int tag)
bool EV_Teleport (int tid, int tag, line_t *line, int side, AActor *thing, bool fog, bool EV_Teleport (int tid, int tag, line_t *line, int side, AActor *thing, bool fog,
bool sourceFog, bool keepOrientation, bool haltVelocity, bool keepHeight) bool sourceFog, bool keepOrientation, bool haltVelocity, bool keepHeight)
{ {
bool predicting = (thing->player && (thing->player->cheats & CF_PREDICTING));
AActor *searcher; AActor *searcher;
fixed_t z; fixed_t z;
angle_t angle = 0; angle_t angle = 0;
@ -342,7 +349,7 @@ bool EV_Teleport (int tid, int tag, line_t *line, int side, AActor *thing, bool
{ // Don't teleport if hit back of line, so you can get out of teleporter. { // Don't teleport if hit back of line, so you can get out of teleporter.
return 0; return 0;
} }
searcher = SelectTeleDest (tid, tag); searcher = SelectTeleDest(tid, tag, predicting);
if (searcher == NULL) if (searcher == NULL)
{ {
return false; return false;
@ -390,7 +397,7 @@ bool EV_Teleport (int tid, int tag, line_t *line, int side, AActor *thing, bool
thing->velx = FixedMul(velx, c) - FixedMul(vely, s); thing->velx = FixedMul(velx, c) - FixedMul(vely, s);
thing->vely = FixedMul(vely, c) + FixedMul(velx, s); thing->vely = FixedMul(vely, c) + FixedMul(velx, s);
} }
if ((velx | vely) == 0 && thing->player != NULL && thing->player->mo == thing) if ((velx | vely) == 0 && thing->player != NULL && thing->player->mo == thing && !predicting)
{ {
thing->player->mo->PlayIdle (); thing->player->mo->PlayIdle ();
} }

View file

@ -62,6 +62,7 @@ static FRandom pr_skullpop ("SkullPop");
// Variables for prediction // Variables for prediction
CVAR (Bool, cl_noprediction, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) CVAR (Bool, cl_noprediction, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
CVAR(Bool, cl_predict_specials, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
static player_t PredictionPlayerBackup; static player_t PredictionPlayerBackup;
static BYTE PredictionActorBackup[sizeof(AActor)]; static BYTE PredictionActorBackup[sizeof(AActor)];
static TArray<sector_t *> PredictionTouchingSectorsBackup; static TArray<sector_t *> PredictionTouchingSectorsBackup;
@ -2722,8 +2723,12 @@ void P_PredictPlayer (player_t *player)
} }
act->BlockNode = NULL; act->BlockNode = NULL;
bool NoInterpolateOld = R_GetViewInterpolationStatus();
for (int i = gametic; i < maxtic; ++i) for (int i = gametic; i < maxtic; ++i)
{ {
if (!NoInterpolateOld)
R_RebuildViewInterpolation(player);
player->cmd = localcmds[i % LOCALCMDTICS]; player->cmd = localcmds[i % LOCALCMDTICS];
P_PlayerThink (player); P_PlayerThink (player);
player->mo->Tick (); player->mo->Tick ();

View file

@ -729,6 +729,42 @@ void R_ClearPastViewer (AActor *actor)
} }
} }
//==========================================================================
//
// R_RebuildViewInterpolation
//
//==========================================================================
void R_RebuildViewInterpolation(player_t *player)
{
if (player == NULL || player->camera == NULL)
return;
if (!NoInterpolateView)
return;
NoInterpolateView = false;
InterpolationViewer *iview = FindPastViewer(player->camera);
iview->oviewx = iview->nviewx;
iview->oviewy = iview->nviewy;
iview->oviewz = iview->nviewz;
iview->oviewpitch = iview->nviewpitch;
iview->oviewangle = iview->nviewangle;
}
//==========================================================================
//
// R_GetViewInterpolationStatus
//
//==========================================================================
bool R_GetViewInterpolationStatus()
{
return NoInterpolateView;
}
//========================================================================== //==========================================================================
// //
// R_SetupFrame // R_SetupFrame

View file

@ -61,6 +61,8 @@ inline angle_t R_PointToAngle (fixed_t x, fixed_t y) { return R_PointToAngle2 (v
subsector_t *R_PointInSubsector (fixed_t x, fixed_t y); subsector_t *R_PointInSubsector (fixed_t x, fixed_t y);
fixed_t R_PointToDist2 (fixed_t dx, fixed_t dy); fixed_t R_PointToDist2 (fixed_t dx, fixed_t dy);
void R_ResetViewInterpolation (); void R_ResetViewInterpolation ();
void R_RebuildViewInterpolation(player_t *player);
bool R_GetViewInterpolationStatus();
void R_SetViewSize (int blocks); void R_SetViewSize (int blocks);
void R_SetFOV (float fov); void R_SetFOV (float fov);
float R_GetFOV (); float R_GetFOV ();

View file

@ -3532,7 +3532,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfInTargetLOS)
//=========================================================================== //===========================================================================
// //
// A_DamageMaster (int amount) // A_DamageMaster (int amount, str damagetype)
// Damages the master of this child by the specified amount. Negative values heal. // Damages the master of this child by the specified amount. Negative values heal.
// //
//=========================================================================== //===========================================================================
@ -3558,7 +3558,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageMaster)
//=========================================================================== //===========================================================================
// //
// A_DamageChildren (amount) // A_DamageChildren (amount, str damagetype)
// Damages the children of this master by the specified amount. Negative values heal. // Damages the children of this master by the specified amount. Negative values heal.
// //
//=========================================================================== //===========================================================================
@ -3592,7 +3592,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageChildren)
//=========================================================================== //===========================================================================
// //
// A_DamageSiblings (amount) // A_DamageSiblings (int amount, str damagetype)
// Damages the siblings of this master by the specified amount. Negative values heal. // Damages the siblings of this master by the specified amount. Negative values heal.
// //
//=========================================================================== //===========================================================================
@ -4994,3 +4994,80 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetSpeed)
self->Speed = speed; self->Speed = speed;
} }
//===========================================================================
//
// A_DamageSelf (int amount, str damagetype)
// Damages the calling actor by the specified amount. Negative values heal.
//
//===========================================================================
DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageSelf)
{
ACTION_PARAM_START(2);
ACTION_PARAM_INT(amount, 0);
ACTION_PARAM_NAME(DamageType, 1);
if (amount > 0)
{
P_DamageMobj(self, self, self, amount, DamageType, DMG_NO_ARMOR);
}
else if (amount < 0)
{
amount = -amount;
P_GiveBody(self, amount);
}
}
//===========================================================================
//
// A_DamageTarget (int amount, str damagetype)
// Damages the target of the actor by the specified amount. Negative values heal.
//
//===========================================================================
DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageTarget)
{
ACTION_PARAM_START(2);
ACTION_PARAM_INT(amount, 0);
ACTION_PARAM_NAME(DamageType, 1);
if (self->target != NULL)
{
if (amount > 0)
{
P_DamageMobj(self->target, self, self, amount, DamageType, DMG_NO_ARMOR);
}
else if (amount < 0)
{
amount = -amount;
P_GiveBody(self->target, amount);
}
}
}
//===========================================================================
//
// A_DamageTracer (int amount, str damagetype)
// Damages the tracer of the actor by the specified amount. Negative values heal.
//
//===========================================================================
DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageTracer)
{
ACTION_PARAM_START(2);
ACTION_PARAM_INT(amount, 0);
ACTION_PARAM_NAME(DamageType, 1);
if (self->target != NULL)
{
if (amount > 0)
{
P_DamageMobj(self->tracer, self, self, amount, DamageType, DMG_NO_ARMOR);
}
else if (amount < 0)
{
amount = -amount;
P_GiveBody(self->tracer, amount);
}
}
}

View file

@ -43,6 +43,8 @@
#include "tarray.h" #include "tarray.h"
#include "thingdef.h" #include "thingdef.h"
#include "thingdef_exp.h" #include "thingdef_exp.h"
#include "actor.h"
#include "actorptrselect.h"
static TMap<FName, FxGlobalFunctionCall::Creator> CreatorMap; static TMap<FName, FxGlobalFunctionCall::Creator> CreatorMap;
@ -185,3 +187,77 @@ public:
GLOBALFUNCTION_ADDER(Sqrt); GLOBALFUNCTION_ADDER(Sqrt);
//==========================================================================
//
// Function: checkclass
//
//==========================================================================
class FxGlobalFunctionCall_CheckClass : public FxGlobalFunctionCall
{
public:
GLOBALFUNCTION_DEFINE(CheckClass);
FxExpression *Resolve(FCompileContext& ctx)
{
CHECKRESOLVED();
if (!ResolveArgs(ctx, 1, 3, false))
return NULL;
for (int i = ArgList->Size(); i > 1;)
{
if (!(*ArgList)[--i]->ValueType.isNumeric())
{
ScriptPosition.Message(MSG_ERROR, "numeric value expected for parameter");
delete this;
return NULL;
}
}
switch ((*ArgList)[0]->ValueType.Type)
{
case VAL_Class: case VAL_Name:break;
default:
ScriptPosition.Message(MSG_ERROR, "actor class expected for parameter");
delete this;
return NULL;
}
ValueType = VAL_Float;
return this;
}
ExpVal EvalExpression(AActor *self)
{
ExpVal ret;
ret.Type = VAL_Int;
const PClass * checkclass;
{
ExpVal v = (*ArgList)[0]->EvalExpression(self);
checkclass = v.GetClass();
if (!checkclass)
{
checkclass = PClass::FindClass(v.GetName());
if (!checkclass) { ret.Int = 0; return ret; }
}
}
bool match_superclass = false;
int pick_pointer = AAPTR_DEFAULT;
switch (ArgList->Size())
{
case 3: match_superclass = (*ArgList)[2]->EvalExpression(self).GetBool();
case 2: pick_pointer = (*ArgList)[1]->EvalExpression(self).GetInt();
}
self = COPY_AAPTR(self, pick_pointer);
if (!self){ ret.Int = 0; return ret; }
ret.Int = match_superclass ? checkclass->IsAncestorOf(self->GetClass()) : checkclass == self->GetClass();
return ret;
}
};
GLOBALFUNCTION_ADDER(CheckClass);

View file

@ -51,7 +51,7 @@ const char *GetVersionString();
// Version identifier for network games. // Version identifier for network games.
// Bump it every time you do a release unless you're certain you // Bump it every time you do a release unless you're certain you
// didn't change anything that will affect sync. // didn't change anything that will affect sync.
#define NETGAMEVERSION 230 #define NETGAMEVERSION 231
// Version stored in the ini's [LastRun] section. // Version stored in the ini's [LastRun] section.
// Bump it if you made some configuration change that you want to // Bump it if you made some configuration change that you want to

View file

@ -304,6 +304,9 @@ ACTOR Actor native //: Thinker
action native A_SetDamageType(name damagetype); action native A_SetDamageType(name damagetype);
action native A_DropItem(class<Actor> item, int dropamount = -1, int chance = 256); action native A_DropItem(class<Actor> item, int dropamount = -1, int chance = 256);
action native A_SetSpeed(float speed); action native A_SetSpeed(float speed);
action native A_DamageSelf(int amount, name damagetype = "none");
action native A_DamageTarget(int amount, name damagetype = "none");
action native A_DamageTracer(int amount, name damagetype = "none");
action native A_CheckSightOrRange(float distance, state label); action native A_CheckSightOrRange(float distance, state label);
action native A_CheckRange(float distance, state label); action native A_CheckRange(float distance, state label);

View file

@ -836,6 +836,7 @@ SCORE_BONUS = "BONUS";
SCORE_COLOR = "COLOR"; SCORE_COLOR = "COLOR";
SCORE_SECRET = "SECRET"; SCORE_SECRET = "SECRET";
SCORE_NAME = "NAME"; SCORE_NAME = "NAME";
SCORE_DELAY = "DELAY(ms)";
SCORE_KILLS = "KILLS"; SCORE_KILLS = "KILLS";
SCORE_FRAGS = "FRAGS"; SCORE_FRAGS = "FRAGS";
SCORE_DEATHS = "DEATHS"; SCORE_DEATHS = "DEATHS";

View file

@ -341,6 +341,7 @@ OptionMenu "OptionsMenu"
Submenu "Automap Options", "AutomapOptions" Submenu "Automap Options", "AutomapOptions"
Submenu "HUD Options", "HUDOptions" Submenu "HUD Options", "HUDOptions"
Submenu "Miscellaneous Options", "MiscOptions" Submenu "Miscellaneous Options", "MiscOptions"
Submenu "Network Options", "NetworkOptions"
Submenu "Sound Options", "SoundOptions" Submenu "Sound Options", "SoundOptions"
Submenu "Display Options", "VideoOptions" Submenu "Display Options", "VideoOptions"
Submenu "Set video mode", "VideoModeMenu" Submenu "Set video mode", "VideoModeMenu"
@ -805,6 +806,13 @@ OptionValue "AltHUDTime"
9, "System" 9, "System"
} }
OptionValue "AltHUDLag"
{
0, "Off"
1, "Netgames only"
2, "Always"
}
OptionMenu "AltHUDOptions" OptionMenu "AltHUDOptions"
{ {
Title "Alternative HUD" Title "Alternative HUD"
@ -819,6 +827,7 @@ OptionMenu "AltHUDOptions"
Option "Show weapons", "hud_showweapons", "OnOff" Option "Show weapons", "hud_showweapons", "OnOff"
Option "Show time", "hud_showtime", "AltHUDTime" Option "Show time", "hud_showtime", "AltHUDTime"
Option "Time color", "hud_timecolor", "TextColors" Option "Time color", "hud_timecolor", "TextColors"
Option "Show network latency", "hud_showlag", "AltHUDLag"
Slider "Red ammo display below %", "hud_ammo_red", 0, 100, 1, 0 Slider "Red ammo display below %", "hud_ammo_red", 0, 100, 1, 0
Slider "Yellow ammo display below %", "hud_ammo_yellow", 0, 100, 1, 0 Slider "Yellow ammo display below %", "hud_ammo_yellow", 0, 100, 1, 0
Slider "Red health display below", "hud_health_red", 0, 100, 1, 0 Slider "Red health display below", "hud_health_red", 0, 100, 1, 0
@ -1591,3 +1600,28 @@ OptionMenu VideoModeMenu
class VideoModeMenu class VideoModeMenu
} }
/*=======================================
*
* Network options menu
*
*=======================================*/
OptionMenu NetworkOptions
{
Title "NETWORK OPTIONS"
StaticText "Local options", 1
Option "Movement prediction", "cl_noprediction", "OffOn"
Option "Predict line actions", "cl_predict_specials", "OnOff"
StaticText " "
StaticText "Host options", 1
Option "Extra Tics", "net_extratic", "ExtraTicMode"
Option "Latency balancing", "net_ticbalance", "OnOff"
}
OptionValue ExtraTicMode
{
0, "None"
1, "1"
2, "All unacknowledged"
}