Merge branch 'net-screen' into 'master'

Splitscreen in netgames

See merge request KartKrew/Kart!22
This commit is contained in:
Sal 2018-10-19 16:01:53 -04:00
commit 2e3f6b09af
17 changed files with 837 additions and 434 deletions

View file

@ -748,6 +748,19 @@ boolean CON_Responder(event_t *ev)
if (modeattacking || metalrecording) if (modeattacking || metalrecording)
return false; return false;
if (ev->data1 >= KEY_MOUSE1) // See also: HUD_Responder
{
INT32 i;
for (i = 0; i < num_gamecontrols; i++)
{
if (gamecontrol[i][0] == ev->data1 || gamecontrol[i][1] == ev->data1)
break;
}
if (i == num_gamecontrols)
return false;
}
if (key == gamecontrol[gc_console][0] || key == gamecontrol[gc_console][1]) if (key == gamecontrol[gc_console][0] || key == gamecontrol[gc_console][1])
{ {
if (consdown) // ignore repeat if (consdown) // ignore repeat

View file

@ -2375,8 +2375,11 @@ static void Command_connect(void)
CONS_Alert(CONS_ERROR, M_GetText("There is no network driver\n")); CONS_Alert(CONS_ERROR, M_GetText("There is no network driver\n"));
} }
splitscreen = 0; if (splitscreen != cv_splitplayers.value-1)
{
splitscreen = cv_splitplayers.value-1;
SplitScreen_OnChange(); SplitScreen_OnChange();
}
botingame = false; botingame = false;
botskin = 0; botskin = 0;
CL_ConnectToServer(viams); CL_ConnectToServer(viams);
@ -2417,14 +2420,15 @@ static void CL_RemovePlayer(INT32 playernum)
if (server && !demoplayback) if (server && !demoplayback)
{ {
INT32 node = playernode[playernum]; INT32 node = playernode[playernum];
//playerpernode[node] = 0; // It'd be better to remove them all at once, but ghosting happened, so continue to let CL_RemovePlayer do it one-by-one
playerpernode[node]--; playerpernode[node]--;
if (playerpernode[node] <= 0) if (playerpernode[node] <= 0)
{ {
// If a resynch was in progress, well, it no longer needs to be. // If a resynch was in progress, well, it no longer needs to be.
SV_InitResynchVars(playernode[playernum]); SV_InitResynchVars(node);
nodeingame[playernode[playernum]] = false; nodeingame[node] = false;
Net_CloseConnection(playernode[playernum]); Net_CloseConnection(node);
ResetNode(node); ResetNode(node);
} }
} }
@ -2759,11 +2763,7 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum)
} }
// Is playernum authorized to make this kick? // Is playernum authorized to make this kick?
if (playernum != serverplayer && !IsPlayerAdmin(playernum) if (playernum != serverplayer && !IsPlayerAdmin(playernum))
&& !(playerpernode[playernode[playernum]] >= 2
&& (nodetoplayer2[playernode[playernum]] == pnum
|| nodetoplayer3[playernode[playernum]] == pnum
|| nodetoplayer4[playernode[playernum]] == pnum)))
{ {
// We received a kick command from someone who isn't the // We received a kick command from someone who isn't the
// server or admin, and who isn't in splitscreen removing // server or admin, and who isn't in splitscreen removing
@ -2775,12 +2775,6 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum)
// "consistency failure" and kicking the offending user // "consistency failure" and kicking the offending user
// instead. // instead.
// Note: Splitscreen in netgames is broken because of
// this. Only the server has any idea of which players
// are using splitscreen on the same computer, so
// clients cannot always determine if a kick is
// legitimate.
CONS_Alert(CONS_WARNING, M_GetText("Illegal kick command received from %s for player %d\n"), player_names[playernum], pnum); CONS_Alert(CONS_WARNING, M_GetText("Illegal kick command received from %s for player %d\n"), player_names[playernum], pnum);
// In debug, print a longer message with more details. // In debug, print a longer message with more details.
@ -2890,7 +2884,7 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum)
break; break;
} }
if (pnum == consoleplayer) if (playernode[pnum] == playernode[consoleplayer])
{ {
#ifdef DUMPCONSISTENCY #ifdef DUMPCONSISTENCY
if (msg == KICK_MSG_CON_FAIL) SV_SavedGame(); if (msg == KICK_MSG_CON_FAIL) SV_SavedGame();
@ -2899,7 +2893,7 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum)
CL_Reset(); CL_Reset();
D_StartTitle(); D_StartTitle();
if (msg == KICK_MSG_CON_FAIL) if (msg == KICK_MSG_CON_FAIL)
M_StartMessage(M_GetText("Server closed connection\n(synch failure)\nPress ESC\n"), NULL, MM_NOTHING); M_StartMessage(M_GetText("Server closed connection\n(Synch failure)\nPress ESC\n"), NULL, MM_NOTHING);
#ifdef NEWPING #ifdef NEWPING
else if (msg == KICK_MSG_PING_HIGH) else if (msg == KICK_MSG_PING_HIGH)
M_StartMessage(M_GetText("Server closed connection\n(Broke ping limit)\nPress ESC\n"), NULL, MM_NOTHING); M_StartMessage(M_GetText("Server closed connection\n(Broke ping limit)\nPress ESC\n"), NULL, MM_NOTHING);
@ -2913,8 +2907,32 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum)
else else
M_StartMessage(M_GetText("You have been kicked by the server\n\nPress ESC\n"), NULL, MM_NOTHING); M_StartMessage(M_GetText("You have been kicked by the server\n\nPress ESC\n"), NULL, MM_NOTHING);
} }
else else if (server)
CL_RemovePlayer(pnum); {
XBOXSTATIC UINT8 buf[0];
// Sal: Because kicks (and a lot of other commands) are player-based, we can't tell which player pnum is on the node from a glance.
// When we want to remove everyone from a node, we have to get the kicked player's node, then remove everyone on that node manually so we don't miss any.
// This avoids the bugs with older SRB2 version's online splitscreen kicks, specifically ghosting.
// On top of this, it can't just be a CL_RemovePlayer call; it has to be a server-sided.
// Clients don't bother setting any nodes for anything but THE server player (even ignoring the server's extra players!), so it'll often remove everyone because they all have node -1/255, insta-desync!
// And yes. This is a netxcmd wrap for just CL_RemovePlayer! :V
#define removethisplayer(otherp) \
if (otherp >= 0) \
{ \
if (otherp != pnum) \
CONS_Printf("\x82%s\x80 left the game (Joined with \x82%s\x80)\n", player_names[otherp], player_names[pnum]); \
buf[0] = (UINT8)otherp; \
SendNetXCmd(XD_REMOVEPLAYER, &buf, 1); \
otherp = -1; \
}
removethisplayer(nodetoplayer[playernode[pnum]])
removethisplayer(nodetoplayer2[playernode[pnum]])
removethisplayer(nodetoplayer3[playernode[pnum]])
removethisplayer(nodetoplayer4[playernode[pnum]])
#undef removethisplayer
}
} }
consvar_t cv_allownewplayer = {"allowjoin", "On", CV_NETVAR, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL }; consvar_t cv_allownewplayer = {"allowjoin", "On", CV_NETVAR, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL };
@ -2937,6 +2955,7 @@ static CV_PossibleValue_t downloadspeed_cons_t[] = {{0, "MIN"}, {32, "MAX"}, {0,
consvar_t cv_downloadspeed = {"downloadspeed", "16", CV_SAVE, downloadspeed_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_downloadspeed = {"downloadspeed", "16", CV_SAVE, downloadspeed_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
static void Got_AddPlayer(UINT8 **p, INT32 playernum); static void Got_AddPlayer(UINT8 **p, INT32 playernum);
static void Got_RemovePlayer(UINT8 **p, INT32 playernum);
// called one time at init // called one time at init
void D_ClientServerInit(void) void D_ClientServerInit(void)
@ -2964,6 +2983,7 @@ void D_ClientServerInit(void)
RegisterNetXCmd(XD_KICK, Got_KickCmd); RegisterNetXCmd(XD_KICK, Got_KickCmd);
RegisterNetXCmd(XD_ADDPLAYER, Got_AddPlayer); RegisterNetXCmd(XD_ADDPLAYER, Got_AddPlayer);
RegisterNetXCmd(XD_REMOVEPLAYER, Got_RemovePlayer);
#ifndef NONET #ifndef NONET
CV_RegisterVar(&cv_allownewplayer); CV_RegisterVar(&cv_allownewplayer);
#ifdef VANILLAJOINNEXTROUND #ifdef VANILLAJOINNEXTROUND
@ -3157,9 +3177,8 @@ static void Got_AddPlayer(UINT8 **p, INT32 playernum)
newplayernum %= MAXPLAYERS; newplayernum %= MAXPLAYERS;
// Clear player before joining, lest some things get set incorrectly // Clear player before joining, lest some things get set incorrectly
// HACK: don't do this for splitscreen, it relies on preset values
if (!splitscreen && !botingame)
CL_ClearPlayer(newplayernum); CL_ClearPlayer(newplayernum);
playeringame[newplayernum] = true; playeringame[newplayernum] = true;
G_AddPlayer(newplayernum); G_AddPlayer(newplayernum);
if (newplayernum+1 > doomcom->numslots) if (newplayernum+1 > doomcom->numslots)
@ -3241,6 +3260,27 @@ static void Got_AddPlayer(UINT8 **p, INT32 playernum)
#endif #endif
} }
// Xcmd XD_REMOVEPLAYER
static void Got_RemovePlayer(UINT8 **p, INT32 playernum)
{
if (playernum != serverplayer && !IsPlayerAdmin(playernum))
{
// protect against hacked/buggy client
CONS_Alert(CONS_WARNING, M_GetText("Illegal remove player command received from %s\n"), player_names[playernum]);
if (server)
{
XBOXSTATIC UINT8 buf[2];
buf[0] = (UINT8)playernum;
buf[1] = KICK_MSG_CON_FAIL;
SendNetXCmd(XD_KICK, &buf, 2);
}
return;
}
CL_RemovePlayer(READUINT8(*p));
}
static boolean SV_AddWaitingPlayers(void) static boolean SV_AddWaitingPlayers(void)
{ {
INT32 node, n, newplayer = false; INT32 node, n, newplayer = false;
@ -3258,58 +3298,6 @@ static boolean SV_AddWaitingPlayers(void)
{ {
newplayer = true; newplayer = true;
if (netgame)
// !!!!!!!!! EXTREMELY SUPER MEGA GIGA ULTRA ULTIMATELY TERRIBLY IMPORTANT !!!!!!!!!
//
// The line just after that comment is an awful, horrible, terrible, TERRIBLE hack.
//
// Basically, the fix I did in order to fix the download freezes happens
// to cause situations in which a player number does not match
// the node number associated to that player.
// That is totally normal, there is absolutely *nothing* wrong with that.
// Really. Player 7 being tied to node 29, for instance, is totally fine.
//
// HOWEVER. A few (broken) parts of the netcode do the TERRIBLE mistake
// of mixing up the concepts of node and player, resulting in
// incorrect handling of cases where a player is tied to a node that has
// a different number (which is a totally normal case, or at least should be).
// This incorrect handling can go as far as literally
// anyone from joining your server at all, forever.
//
// Given those two facts, there are two options available
// in order to let this download freeze fix be:
// 1) Fix the broken parts that assume a node is a player or similar bullshit.
// 2) Change the part this comment is located at, so that any player who joins
// is given the same number as their associated node.
//
// No need to say, 1) is by far the obvious best, whereas 2) is a terrible hack.
// Unfortunately, after trying 1), I most likely didn't manage to find all
// of those broken parts, and thus 2) has become the only safe option that remains.
//
// So I did this hack.
//
// If it isn't clear enough, in order to get rid of this ugly hack,
// you will have to fix all parts of the netcode that
// make a confusion between nodes and players.
//
// And if it STILL isn't clear enough, a node and a player
// is NOT the same thing. Never. NEVER. *NEVER*.
//
// And if someday you make the terrible mistake of
// daring to have the unforgivable idea to try thinking
// that a node might possibly be the same as a player,
// or that a player should have the same number as its node,
// be sure that I will somehow know about it and
// hunt you down tirelessly and make you regret it,
// even if you live on the other side of the world.
//
// TODO: vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
// \todo >>>>>>>>>> Remove this horrible hack as soon as possible <<<<<<<<<<
// TODO: ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
//
// !!!!!!!!! EXTREMELY SUPER MEGA GIGA ULTRA ULTIMATELY TERRIBLY IMPORTANT !!!!!!!!!
newplayernum = node; // OMFG SAY WELCOME TO TEH NEW HACK FOR FIX FIL DOWNLOAD!!1!
else // Don't use the hack if we don't have to
// search for a free playernum // search for a free playernum
// we can't use playeringame since it is not updated here // we can't use playeringame since it is not updated here
for (; newplayernum < MAXPLAYERS; newplayernum++) for (; newplayernum < MAXPLAYERS; newplayernum++)
@ -3495,7 +3483,7 @@ static void HandleConnect(SINT8 node)
SV_SendRefuse(node, M_GetText("The server is not accepting\njoins for the moment")); SV_SendRefuse(node, M_GetText("The server is not accepting\njoins for the moment"));
else if (D_NumPlayers() >= cv_maxplayers.value) else if (D_NumPlayers() >= cv_maxplayers.value)
SV_SendRefuse(node, va(M_GetText("Maximum players reached: %d"), cv_maxplayers.value)); SV_SendRefuse(node, va(M_GetText("Maximum players reached: %d"), cv_maxplayers.value));
else if (netgame && netbuffer->u.clientcfg.localplayers > 1) // Hacked client? else if (netgame && netbuffer->u.clientcfg.localplayers > 4) // Hacked client?
SV_SendRefuse(node, M_GetText("Too many players from\nthis node.")); SV_SendRefuse(node, M_GetText("Too many players from\nthis node."));
else if (netgame && !netbuffer->u.clientcfg.localplayers) // Stealth join? else if (netgame && !netbuffer->u.clientcfg.localplayers) // Stealth join?
SV_SendRefuse(node, M_GetText("No players from\nthis node.")); SV_SendRefuse(node, M_GetText("No players from\nthis node."));
@ -3529,8 +3517,8 @@ static void HandleConnect(SINT8 node)
{ {
G_SetGamestate(backupstate); G_SetGamestate(backupstate);
/// \note Shouldn't SV_SendRefuse be called before ResetNode? /// \note Shouldn't SV_SendRefuse be called before ResetNode?
ResetNode(node);
SV_SendRefuse(node, M_GetText("Server couldn't send info, please try again")); SV_SendRefuse(node, M_GetText("Server couldn't send info, please try again"));
ResetNode(node); // Yeah, lets try it!
/// \todo fix this !!! /// \todo fix this !!!
return; // restart the while return; // restart the while
} }
@ -3983,10 +3971,10 @@ FILESTAMP
--resynch_score[node]; --resynch_score[node];
break; break;
case PT_TEXTCMD: case PT_TEXTCMD:
case PT_TEXTCMD2: // splitscreen special case PT_TEXTCMD2:
case PT_TEXTCMD3: case PT_TEXTCMD3:
case PT_TEXTCMD4: case PT_TEXTCMD4:
if (netbuffer->packettype == PT_TEXTCMD2) if (netbuffer->packettype == PT_TEXTCMD2) // splitscreen special
netconsole = nodetoplayer2[node]; netconsole = nodetoplayer2[node];
else if (netbuffer->packettype == PT_TEXTCMD3) else if (netbuffer->packettype == PT_TEXTCMD3)
netconsole = nodetoplayer3[node]; netconsole = nodetoplayer3[node];
@ -4073,9 +4061,9 @@ FILESTAMP
else else
buf[1] = KICK_MSG_PLAYER_QUIT; buf[1] = KICK_MSG_PLAYER_QUIT;
SendNetXCmd(XD_KICK, &buf, 2); SendNetXCmd(XD_KICK, &buf, 2);
nodetoplayer[node] = -1; //nodetoplayer[node] = -1;
if (nodetoplayer2[node] != -1 && nodetoplayer2[node] >= 0 /*if (nodetoplayer2[node] != -1 && nodetoplayer2[node] >= 0
&& playeringame[(UINT8)nodetoplayer2[node]]) && playeringame[(UINT8)nodetoplayer2[node]])
{ {
buf[0] = nodetoplayer2[node]; buf[0] = nodetoplayer2[node];
@ -4097,7 +4085,7 @@ FILESTAMP
buf[0] = nodetoplayer4[node]; buf[0] = nodetoplayer4[node];
SendNetXCmd(XD_KICK, &buf, 2); SendNetXCmd(XD_KICK, &buf, 2);
nodetoplayer4[node] = -1; nodetoplayer4[node] = -1;
} }*/
} }
Net_CloseConnection(node); Net_CloseConnection(node);
nodeingame[node] = false; nodeingame[node] = false;

View file

@ -1362,11 +1362,26 @@ void D_SRB2Main(void)
#endif #endif
} }
// Set up splitscreen players before joining!
if (!dedicated && (M_CheckParm("-splitscreen") && M_IsNextParm()))
{
UINT8 num = atoi(M_GetNextParm());
if (num >= 1 && num <= 4)
{
CV_StealthSetValue(&cv_splitplayers, num);
splitscreen = num-1;
SplitScreen_OnChange();
}
}
// init all NETWORK // init all NETWORK
CONS_Printf("D_CheckNetGame(): Checking network game status.\n"); CONS_Printf("D_CheckNetGame(): Checking network game status.\n");
if (D_CheckNetGame()) if (D_CheckNetGame())
autostart = true; autostart = true;
if (splitscreen) // Make sure multiplayer & autostart is set if you have splitscreen, even after D_CheckNetGame
multiplayer = autostart = true;
// check for a driver that wants intermission stats // check for a driver that wants intermission stats
// start the apropriate game based on parms // start the apropriate game based on parms
if (M_CheckParm("-metal")) if (M_CheckParm("-metal"))

View file

@ -375,6 +375,7 @@ consvar_t cv_kartdebugdistribution = {"kartdebugdistribution", "Off", CV_NETVAR|
consvar_t cv_kartdebughuddrop = {"kartdebughuddrop", "Off", CV_NETVAR|CV_CHEAT, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_kartdebughuddrop = {"kartdebughuddrop", "Off", CV_NETVAR|CV_CHEAT, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_kartdebugcheckpoint = {"kartdebugcheckpoint", "Off", 0, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_kartdebugcheckpoint = {"kartdebugcheckpoint", "Off", 0, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_kartdebugnodes = {"kartdebugnodes", "Off", 0, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
static CV_PossibleValue_t votetime_cons_t[] = {{10, "MIN"}, {3600, "MAX"}, {0, NULL}}; static CV_PossibleValue_t votetime_cons_t[] = {{10, "MIN"}, {3600, "MAX"}, {0, NULL}};
consvar_t cv_votetime = {"votetime", "20", CV_NETVAR, votetime_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_votetime = {"votetime", "20", CV_NETVAR, votetime_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
@ -753,6 +754,8 @@ void D_RegisterClientCommands(void)
CV_RegisterVar(&cv_playername4); CV_RegisterVar(&cv_playername4);
CV_RegisterVar(&cv_playercolor4); CV_RegisterVar(&cv_playercolor4);
CV_RegisterVar(&cv_skin4); CV_RegisterVar(&cv_skin4);
// preferred number of players
CV_RegisterVar(&cv_splitplayers);
#ifdef SEENAMES #ifdef SEENAMES
CV_RegisterVar(&cv_seenames); CV_RegisterVar(&cv_seenames);
@ -1345,16 +1348,23 @@ static void SendNameAndColor(void)
// splitscreen // splitscreen
static void SendNameAndColor2(void) static void SendNameAndColor2(void)
{ {
INT32 secondplaya; INT32 secondplaya = -1;
XBOXSTATIC char buf[MAXPLAYERNAME+2];
char *p;
if (splitscreen < 1 && !botingame) if (splitscreen < 1 && !botingame)
return; // can happen if skin2/color2/name2 changed return; // can happen if skin2/color2/name2 changed
if (secondarydisplayplayer != consoleplayer) if (secondarydisplayplayer != consoleplayer)
secondplaya = secondarydisplayplayer; secondplaya = secondarydisplayplayer;
else // HACK else if (!netgame) // HACK
secondplaya = 1; secondplaya = 1;
if (secondplaya == -1)
return;
p = buf;
// normal player colors // normal player colors
if (G_GametypeHasTeams()) if (G_GametypeHasTeams())
{ {
@ -1431,21 +1441,53 @@ static void SendNameAndColor2(void)
return; return;
} }
// Don't actually send anything because splitscreen isn't actually allowed in netgames anyway! snac2pending++;
// Don't change name if muted
if (cv_mute.value && !(server || IsPlayerAdmin(secondarydisplayplayer)))
CV_StealthSet(&cv_playername2, player_names[secondarydisplayplayer]);
else // Cleanup name if changing it
CleanupPlayerName(secondarydisplayplayer, cv_playername2.zstring);
// Don't change skin if the server doesn't want you to.
if (!CanChangeSkin(secondarydisplayplayer))
CV_StealthSet(&cv_skin2, skins[players[secondarydisplayplayer].skin].name);
// check if player has the skin loaded (cv_skin2 may have
// the name of a skin that was available in the previous game)
cv_skin2.value = R_SkinAvailable(cv_skin2.string);
if (cv_skin2.value < 0)
{
CV_StealthSet(&cv_skin2, DEFAULTSKIN);
cv_skin2.value = 0;
}
// Finally write out the complete packet and send it off.
WRITESTRINGN(p, cv_playername2.zstring, MAXPLAYERNAME);
WRITEUINT8(p, (UINT8)cv_playercolor2.value);
WRITEUINT8(p, (UINT8)cv_skin2.value);
SendNetXCmd2(XD_NAMEANDCOLOR, buf, p - buf);
} }
static void SendNameAndColor3(void) static void SendNameAndColor3(void)
{ {
INT32 thirdplaya; INT32 thirdplaya = -1;
XBOXSTATIC char buf[MAXPLAYERNAME+2];
char *p;
if (splitscreen < 2) if (splitscreen < 2)
return; // can happen if skin3/color3/name3 changed return; // can happen if skin3/color3/name3 changed
if (thirddisplayplayer != consoleplayer) if (thirddisplayplayer != consoleplayer)
thirdplaya = thirddisplayplayer; thirdplaya = thirddisplayplayer;
else // HACK else if (!netgame) // HACK
thirdplaya = 2; thirdplaya = 2;
if (thirdplaya == -1)
return;
p = buf;
// normal player colors // normal player colors
if (G_GametypeHasTeams()) if (G_GametypeHasTeams())
{ {
@ -1514,21 +1556,53 @@ static void SendNameAndColor3(void)
return; return;
} }
// Don't actually send anything because splitscreen isn't actually allowed in netgames anyway! snac3pending++;
// Don't change name if muted
if (cv_mute.value && !(server || IsPlayerAdmin(thirddisplayplayer)))
CV_StealthSet(&cv_playername3, player_names[thirddisplayplayer]);
else // Cleanup name if changing it
CleanupPlayerName(thirddisplayplayer, cv_playername3.zstring);
// Don't change skin if the server doesn't want you to.
if (!CanChangeSkin(thirddisplayplayer))
CV_StealthSet(&cv_skin3, skins[players[thirddisplayplayer].skin].name);
// check if player has the skin loaded (cv_skin3 may have
// the name of a skin that was available in the previous game)
cv_skin3.value = R_SkinAvailable(cv_skin3.string);
if (cv_skin3.value < 0)
{
CV_StealthSet(&cv_skin3, DEFAULTSKIN);
cv_skin3.value = 0;
}
// Finally write out the complete packet and send it off.
WRITESTRINGN(p, cv_playername3.zstring, MAXPLAYERNAME);
WRITEUINT8(p, (UINT8)cv_playercolor3.value);
WRITEUINT8(p, (UINT8)cv_skin3.value);
SendNetXCmd3(XD_NAMEANDCOLOR, buf, p - buf);
} }
static void SendNameAndColor4(void) static void SendNameAndColor4(void)
{ {
INT32 fourthplaya; INT32 fourthplaya = -1;
XBOXSTATIC char buf[MAXPLAYERNAME+2];
char *p;
if (splitscreen < 3) if (splitscreen < 3)
return; // can happen if skin4/color4/name4 changed return; // can happen if skin4/color4/name4 changed
if (fourthdisplayplayer != consoleplayer) if (fourthdisplayplayer != consoleplayer)
fourthplaya = fourthdisplayplayer; fourthplaya = fourthdisplayplayer;
else // HACK else if (!netgame) // HACK
fourthplaya = 3; fourthplaya = 3;
if (fourthplaya == -1)
return;
p = buf;
// normal player colors // normal player colors
if (G_GametypeHasTeams()) if (G_GametypeHasTeams())
{ {
@ -1605,7 +1679,32 @@ static void SendNameAndColor4(void)
return; return;
} }
// Don't actually send anything because splitscreen isn't actually allowed in netgames anyway! snac4pending++;
// Don't change name if muted
if (cv_mute.value && !(server || IsPlayerAdmin(fourthdisplayplayer)))
CV_StealthSet(&cv_playername4, player_names[fourthdisplayplayer]);
else // Cleanup name if changing it
CleanupPlayerName(fourthdisplayplayer, cv_playername4.zstring);
// Don't change skin if the server doesn't want you to.
if (!CanChangeSkin(fourthdisplayplayer))
CV_StealthSet(&cv_skin4, skins[players[fourthdisplayplayer].skin].name);
// check if player has the skin loaded (cv_skin4 may have
// the name of a skin that was available in the previous game)
cv_skin4.value = R_SkinAvailable(cv_skin4.string);
if (cv_skin4.value < 0)
{
CV_StealthSet(&cv_skin4, DEFAULTSKIN);
cv_skin4.value = 0;
}
// Finally write out the complete packet and send it off.
WRITESTRINGN(p, cv_playername4.zstring, MAXPLAYERNAME);
WRITEUINT8(p, (UINT8)cv_playercolor4.value);
WRITEUINT8(p, (UINT8)cv_skin4.value);
SendNetXCmd4(XD_NAMEANDCOLOR, buf, p - buf);
} }
static void Got_NameAndColor(UINT8 **cp, INT32 playernum) static void Got_NameAndColor(UINT8 **cp, INT32 playernum)
@ -2005,16 +2104,20 @@ void D_SetupVote(void)
void D_ModifyClientVote(SINT8 voted, UINT8 splitplayer) void D_ModifyClientVote(SINT8 voted, UINT8 splitplayer)
{ {
char buf[1]; char buf[2];
char *p = buf; char *p = buf;
UINT8 player = consoleplayer;
if (splitplayer == 1)
player = secondarydisplayplayer;
else if (splitplayer == 2)
player = thirddisplayplayer;
else if (splitplayer == 3)
player = fourthdisplayplayer;
if (splitplayer > 0) // Don't actually send anything for splitscreen
votes[splitplayer] = voted;
else
{
WRITESINT8(p, voted); WRITESINT8(p, voted);
SendNetXCmd(XD_MODIFYVOTE, &buf, 1); WRITEUINT8(p, player);
} SendNetXCmd(XD_MODIFYVOTE, &buf, 2);
} }
void D_PickVote(void) void D_PickVote(void)
@ -4631,7 +4734,10 @@ static void Got_SetupVotecmd(UINT8 **cp, INT32 playernum)
static void Got_ModifyVotecmd(UINT8 **cp, INT32 playernum) static void Got_ModifyVotecmd(UINT8 **cp, INT32 playernum)
{ {
SINT8 voted = READSINT8(*cp); SINT8 voted = READSINT8(*cp);
votes[playernum] = voted; UINT8 p = READUINT8(*cp);
(void)playernum;
votes[p] = voted;
} }
static void Got_PickVotecmd(UINT8 **cp, INT32 playernum) static void Got_PickVotecmd(UINT8 **cp, INT32 playernum)

View file

@ -132,7 +132,7 @@ extern consvar_t cv_karteliminatelast;
extern consvar_t cv_votetime; extern consvar_t cv_votetime;
extern consvar_t cv_kartdebugitem, cv_kartdebugamount, cv_kartdebugshrink, cv_kartdebugdistribution, cv_kartdebughuddrop; extern consvar_t cv_kartdebugitem, cv_kartdebugamount, cv_kartdebugshrink, cv_kartdebugdistribution, cv_kartdebughuddrop;
extern consvar_t cv_kartdebugcheckpoint; extern consvar_t cv_kartdebugcheckpoint, cv_kartdebugnodes;
extern consvar_t cv_itemfinder; extern consvar_t cv_itemfinder;
@ -195,9 +195,10 @@ typedef enum
XD_SETUPVOTE, // 22 XD_SETUPVOTE, // 22
XD_MODIFYVOTE, // 23 XD_MODIFYVOTE, // 23
XD_PICKVOTE, // 24 XD_PICKVOTE, // 24
XD_REMOVEPLAYER,// 25
#ifdef HAVE_BLUA #ifdef HAVE_BLUA
XD_LUACMD, // 25 XD_LUACMD, // 26
XD_LUAVAR, // 26 XD_LUAVAR, // 27
#endif #endif
MAXNETXCMD MAXNETXCMD
} netxcmd_t; } netxcmd_t;

View file

@ -3122,7 +3122,7 @@ boolean G_GametypeHasSpectators(void)
#if 0 #if 0
return (gametype != GT_COOP && gametype != GT_COMPETITION && gametype != GT_RACE); return (gametype != GT_COOP && gametype != GT_COMPETITION && gametype != GT_RACE);
#else #else
return (!splitscreen);//true; return (netgame); //true
#endif #endif
} }

View file

@ -62,6 +62,7 @@ extern consvar_t cv_turnaxis2,cv_moveaxis2,cv_brakeaxis2,cv_aimaxis2,cv_lookaxis
extern consvar_t cv_turnaxis3,cv_moveaxis3,cv_brakeaxis3,cv_aimaxis3,cv_lookaxis3,cv_fireaxis3,cv_driftaxis3; extern consvar_t cv_turnaxis3,cv_moveaxis3,cv_brakeaxis3,cv_aimaxis3,cv_lookaxis3,cv_fireaxis3,cv_driftaxis3;
extern consvar_t cv_turnaxis4,cv_moveaxis4,cv_brakeaxis4,cv_aimaxis4,cv_lookaxis4,cv_fireaxis4,cv_driftaxis4; extern consvar_t cv_turnaxis4,cv_moveaxis4,cv_brakeaxis4,cv_aimaxis4,cv_lookaxis4,cv_fireaxis4,cv_driftaxis4;
extern consvar_t cv_ghost_besttime, cv_ghost_bestlap, cv_ghost_last, cv_ghost_guest, cv_ghost_staff; extern consvar_t cv_ghost_besttime, cv_ghost_bestlap, cv_ghost_last, cv_ghost_guest, cv_ghost_staff;
extern consvar_t cv_splitplayers;
typedef enum typedef enum
{ {

View file

@ -1066,6 +1066,26 @@ boolean HU_Responder(event_t *ev)
// only KeyDown events now... // only KeyDown events now...
// Shoot, to prevent P1 chatting from ruining the game for everyone else, it's either:
// A. completely disallow opening chat entirely in online splitscreen
// or B. iterate through all controls to make sure it's bound to player 1 before eating
// You can see which one I chose.
// (Unless if you're sharing a keyboard, since you probably establish when you start chatting that you have dibs on it...)
// (Ahhh, the good ol days when I was a kid who couldn't afford an extra USB controller...)
if (ev->data1 >= KEY_MOUSE1)
{
INT32 i;
for (i = 0; i < num_gamecontrols; i++)
{
if (gamecontrol[i][0] == ev->data1 || gamecontrol[i][1] == ev->data1)
break;
}
if (i == num_gamecontrols)
return false;
}
if (!chat_on) if (!chat_on)
{ {
// enter chat mode // enter chat mode
@ -1269,6 +1289,7 @@ static void HU_drawMiniChat(void)
{ {
INT32 x = chatx+2; INT32 x = chatx+2;
INT32 charwidth = 4, charheight = 6; INT32 charwidth = 4, charheight = 6;
INT32 boxw = cv_chatwidth.value;
INT32 dx = 0, dy = 0; INT32 dx = 0, dy = 0;
size_t i = chat_nummsg_min; size_t i = chat_nummsg_min;
boolean prev_linereturn = false; // a hack to prevent double \n while I have no idea why they happen in the first place. boolean prev_linereturn = false; // a hack to prevent double \n while I have no idea why they happen in the first place.
@ -1280,9 +1301,12 @@ static void HU_drawMiniChat(void)
if (!chat_nummsg_min) if (!chat_nummsg_min)
return; // needless to say it's useless to do anything if we don't have anything to draw. return; // needless to say it's useless to do anything if we don't have anything to draw.
if (splitscreen > 1)
boxw = max(64, boxw/2);
for (; i>0; i--) for (; i>0; i--)
{ {
const char *msg = CHAT_WordWrap(x+2, cv_chatwidth.value-(charwidth*2), V_SNAPTOBOTTOM|V_SNAPTOLEFT|V_ALLOWLOWERCASE, chat_mini[i-1]); const char *msg = CHAT_WordWrap(x+2, boxw-(charwidth*2), V_SNAPTOBOTTOM|V_SNAPTOLEFT|V_ALLOWLOWERCASE, chat_mini[i-1]);
size_t j = 0; size_t j = 0;
INT32 linescount = 0; INT32 linescount = 0;
@ -1315,7 +1339,7 @@ static void HU_drawMiniChat(void)
} }
prev_linereturn = false; prev_linereturn = false;
dx += charwidth; dx += charwidth;
if (dx >= cv_chatwidth.value) if (dx >= boxw)
{ {
dx = 0; dx = 0;
linescount += 1; linescount += 1;
@ -1326,7 +1350,17 @@ static void HU_drawMiniChat(void)
msglines += linescount+1; msglines += linescount+1;
} }
y = chaty - charheight*(msglines+1) - (cv_kartspeedometer.value ? 16 : 0); y = chaty - charheight*(msglines+1);
if (splitscreen)
{
y -= BASEVIDHEIGHT/2;
if (splitscreen > 1)
y += 16;
}
else
y -= (cv_kartspeedometer.value ? 16 : 0);
dx = 0; dx = 0;
dy = 0; dy = 0;
i = 0; i = 0;
@ -1338,7 +1372,7 @@ static void HU_drawMiniChat(void)
INT32 timer = ((cv_chattime.value*TICRATE)-chat_timers[i]) - cv_chattime.value*TICRATE+9; // see below... INT32 timer = ((cv_chattime.value*TICRATE)-chat_timers[i]) - cv_chattime.value*TICRATE+9; // see below...
INT32 transflag = (timer >= 0 && timer <= 9) ? (timer*V_10TRANS) : 0; // you can make bad jokes out of this one. INT32 transflag = (timer >= 0 && timer <= 9) ? (timer*V_10TRANS) : 0; // you can make bad jokes out of this one.
size_t j = 0; size_t j = 0;
const char *msg = CHAT_WordWrap(x+2, cv_chatwidth.value-(charwidth*2), V_SNAPTOBOTTOM|V_SNAPTOLEFT|V_ALLOWLOWERCASE, chat_mini[i]); // get the current message, and word wrap it. const char *msg = CHAT_WordWrap(x+2, boxw-(charwidth*2), V_SNAPTOBOTTOM|V_SNAPTOLEFT|V_ALLOWLOWERCASE, chat_mini[i]); // get the current message, and word wrap it.
UINT8 *colormap = NULL; UINT8 *colormap = NULL;
while(msg[j]) // iterate through msg while(msg[j]) // iterate through msg
@ -1376,7 +1410,7 @@ static void HU_drawMiniChat(void)
dx += charwidth; dx += charwidth;
prev_linereturn = false; prev_linereturn = false;
if (dx >= cv_chatwidth.value) if (dx >= boxw)
{ {
dx = 0; dx = 0;
dy += charheight; dy += charheight;
@ -1397,6 +1431,7 @@ static void HU_drawMiniChat(void)
static void HU_drawChatLog(INT32 offset) static void HU_drawChatLog(INT32 offset)
{ {
INT32 charwidth = 4, charheight = 6; INT32 charwidth = 4, charheight = 6;
INT32 boxw = cv_chatwidth.value, boxh = cv_chatheight.value;
INT32 x = chatx+2, y, dx = 0, dy = 0; INT32 x = chatx+2, y, dx = 0, dy = 0;
UINT32 i = 0; UINT32 i = 0;
INT32 chat_topy, chat_bottomy; INT32 chat_topy, chat_bottomy;
@ -1406,17 +1441,34 @@ static void HU_drawChatLog(INT32 offset)
if (chat_scroll > chat_maxscroll) if (chat_scroll > chat_maxscroll)
chat_scroll = chat_maxscroll; chat_scroll = chat_maxscroll;
y = chaty - offset*charheight - (chat_scroll*charheight) - cv_chatheight.value*charheight - 12 - (cv_kartspeedometer.value ? 16 : 0); if (splitscreen)
chat_topy = y + chat_scroll*charheight; {
chat_bottomy = chat_topy + cv_chatheight.value*charheight; boxh = max(6, boxh/2);
if (splitscreen > 1)
boxw = max(64, boxw/2);
}
V_DrawFillConsoleMap(chatx, chat_topy, cv_chatwidth.value, cv_chatheight.value*charheight +2, 239|V_SNAPTOBOTTOM|V_SNAPTOLEFT); // log box y = chaty - offset*charheight - (chat_scroll*charheight) - boxh*charheight - 12;
if (splitscreen)
{
y -= BASEVIDHEIGHT/2;
if (splitscreen > 1)
y += 16;
}
else
y -= (cv_kartspeedometer.value ? 16 : 0);
chat_topy = y + chat_scroll*charheight;
chat_bottomy = chat_topy + boxh*charheight;
V_DrawFillConsoleMap(chatx, chat_topy, boxw, boxh*charheight +2, 239|V_SNAPTOBOTTOM|V_SNAPTOLEFT); // log box
for (i=0; i<chat_nummsg_log; i++) // iterate through our chatlog for (i=0; i<chat_nummsg_log; i++) // iterate through our chatlog
{ {
INT32 clrflag = 0; INT32 clrflag = 0;
INT32 j = 0; INT32 j = 0;
const char *msg = CHAT_WordWrap(x+2, cv_chatwidth.value-(charwidth*2), V_SNAPTOBOTTOM|V_SNAPTOLEFT|V_ALLOWLOWERCASE, chat_log[i]); // get the current message, and word wrap it. const char *msg = CHAT_WordWrap(x+2, boxw-(charwidth*2), V_SNAPTOBOTTOM|V_SNAPTOLEFT|V_ALLOWLOWERCASE, chat_log[i]); // get the current message, and word wrap it.
UINT8 *colormap = NULL; UINT8 *colormap = NULL;
while(msg[j]) // iterate through msg while(msg[j]) // iterate through msg
{ {
@ -1448,7 +1500,7 @@ static void HU_drawChatLog(INT32 offset)
} }
dx += charwidth; dx += charwidth;
if (dx >= cv_chatwidth.value-charwidth-2 && i<chat_nummsg_log && msg[j] >= HU_FONTSTART) // end of message shouldn't count, nor should invisible characters!!!! if (dx >= boxw-charwidth-2 && i<chat_nummsg_log && msg[j] >= HU_FONTSTART) // end of message shouldn't count, nor should invisible characters!!!!
{ {
dx = 0; dx = 0;
dy += charheight; dy += charheight;
@ -1466,10 +1518,10 @@ static void HU_drawChatLog(INT32 offset)
// getmaxscroll through a lazy hack. We do all these loops, so let's not do more loops that are gonna lag the game more. :P // getmaxscroll through a lazy hack. We do all these loops, so let's not do more loops that are gonna lag the game more. :P
chat_maxscroll = (dy/charheight); // welcome to C, we don't know what min() and max() are. chat_maxscroll = (dy/charheight); // welcome to C, we don't know what min() and max() are.
if (chat_maxscroll <= (UINT32)cv_chatheight.value) if (chat_maxscroll <= (UINT32)boxh)
chat_maxscroll = 0; chat_maxscroll = 0;
else else
chat_maxscroll -= cv_chatheight.value; chat_maxscroll -= boxh;
// if we're not bound by the time, autoscroll for next frame: // if we're not bound by the time, autoscroll for next frame:
if (atbottom) if (atbottom)
@ -1502,13 +1554,26 @@ static INT16 typelines = 1; // number of drawfill lines we need. it's some weird
static void HU_DrawChat(void) static void HU_DrawChat(void)
{ {
INT32 charwidth = 4, charheight = 6; INT32 charwidth = 4, charheight = 6;
INT32 t = 0, c = 0, y = chaty - (typelines*charheight) - (cv_kartspeedometer.value ? 16 : 0); INT32 boxw = cv_chatwidth.value;
INT32 t = 0, c = 0, y = chaty - (typelines*charheight);
UINT32 i = 0, saylen = strlen(w_chat); // You learn new things everyday! UINT32 i = 0, saylen = strlen(w_chat); // You learn new things everyday!
INT32 cflag = 0; INT32 cflag = 0;
const char *ntalk = "Say: ", *ttalk = "Team: "; const char *ntalk = "Say: ", *ttalk = "Team: ";
const char *talk = ntalk; const char *talk = ntalk;
const char *mute = "Chat has been muted."; const char *mute = "Chat has been muted.";
if (splitscreen)
{
y -= BASEVIDHEIGHT/2;
if (splitscreen > 1)
{
y += 16;
boxw = max(64, boxw/2);
}
}
else
y -= (cv_kartspeedometer.value ? 16 : 0);
if (teamtalk) if (teamtalk)
{ {
talk = ttalk; talk = ttalk;
@ -1527,7 +1592,7 @@ static void HU_DrawChat(void)
cflag = V_GRAYMAP; // set text in gray if chat is muted. cflag = V_GRAYMAP; // set text in gray if chat is muted.
} }
V_DrawFillConsoleMap(chatx, y-1, cv_chatwidth.value, (typelines*charheight), 239 | V_SNAPTOBOTTOM | V_SNAPTOLEFT); V_DrawFillConsoleMap(chatx, y-1, boxw, (typelines*charheight), 239 | V_SNAPTOBOTTOM | V_SNAPTOLEFT);
while (talk[i]) while (talk[i])
{ {
@ -1560,7 +1625,7 @@ static void HU_DrawChat(void)
boolean skippedline = false; boolean skippedline = false;
if (c_input == (i+1)) if (c_input == (i+1))
{ {
int cursorx = (c+charwidth < cv_chatwidth.value-charwidth) ? (chatx + 2 + c+charwidth) : (chatx+1); // we may have to go down. int cursorx = (c+charwidth < boxw-charwidth) ? (chatx + 2 + c+charwidth) : (chatx+1); // we may have to go down.
int cursory = (cursorx != chatx+1) ? (y) : (y+charheight); int cursory = (cursorx != chatx+1) ? (y) : (y+charheight);
if (hu_tick < 4) if (hu_tick < 4)
V_DrawChatCharacter(cursorx, cursory+1, '_' |V_SNAPTOBOTTOM|V_SNAPTOLEFT|t, !cv_allcaps.value, NULL); V_DrawChatCharacter(cursorx, cursory+1, '_' |V_SNAPTOBOTTOM|V_SNAPTOLEFT|t, !cv_allcaps.value, NULL);
@ -1580,7 +1645,7 @@ static void HU_DrawChat(void)
V_DrawChatCharacter(chatx + c + 2, y, w_chat[i++] | V_SNAPTOBOTTOM|V_SNAPTOLEFT | t, !cv_allcaps.value, NULL); V_DrawChatCharacter(chatx + c + 2, y, w_chat[i++] | V_SNAPTOBOTTOM|V_SNAPTOLEFT | t, !cv_allcaps.value, NULL);
c += charwidth; c += charwidth;
if (c > cv_chatwidth.value-(charwidth*2) && !skippedline) if (c > boxw-(charwidth*2) && !skippedline)
{ {
c = 0; c = 0;
y += charheight; y += charheight;
@ -1593,6 +1658,14 @@ static void HU_DrawChat(void)
{ {
INT32 count = 0; INT32 count = 0;
INT32 p_dispy = chaty - charheight -1; INT32 p_dispy = chaty - charheight -1;
if (splitscreen)
{
p_dispy -= BASEVIDHEIGHT/2;
if (splitscreen > 1)
p_dispy += 16;
}
else
p_dispy -= (cv_kartspeedometer.value ? 16 : 0);
i = 0; i = 0;
for(i=0; (i<MAXPLAYERS); i++) for(i=0; (i<MAXPLAYERS); i++)
{ {
@ -1643,8 +1716,8 @@ static void HU_DrawChat(void)
{ {
char name[MAXPLAYERNAME+1]; char name[MAXPLAYERNAME+1];
strlcpy(name, player_names[i], 7); // shorten name to 7 characters. strlcpy(name, player_names[i], 7); // shorten name to 7 characters.
V_DrawFillConsoleMap(chatx+ cv_chatwidth.value + 2, p_dispy- (6*count), 48, 6, 239 | V_SNAPTOBOTTOM | V_SNAPTOLEFT); // fill it like the chat so the text doesn't become hard to read because of the hud. V_DrawFillConsoleMap(chatx+ boxw + 2, p_dispy- (6*count), 48, 6, 239 | V_SNAPTOBOTTOM | V_SNAPTOLEFT); // fill it like the chat so the text doesn't become hard to read because of the hud.
V_DrawSmallString(chatx+ cv_chatwidth.value + 4, p_dispy- (6*count), V_SNAPTOBOTTOM|V_SNAPTOLEFT|V_ALLOWLOWERCASE, va("\x82%d\x80 - %s", i, name)); V_DrawSmallString(chatx+ boxw + 4, p_dispy- (6*count), V_SNAPTOBOTTOM|V_SNAPTOLEFT|V_ALLOWLOWERCASE, va("\x82%d\x80 - %s", i, name));
count++; count++;
} }
} }

View file

@ -434,6 +434,7 @@ void K_RegisterKartStuff(void)
CV_RegisterVar(&cv_kartdebughuddrop); CV_RegisterVar(&cv_kartdebughuddrop);
CV_RegisterVar(&cv_kartdebugcheckpoint); CV_RegisterVar(&cv_kartdebugcheckpoint);
CV_RegisterVar(&cv_kartdebugnodes);
} }
//} //}
@ -895,7 +896,35 @@ static void K_KartItemRoulette(player_t *player, ticcmd_t *cmd)
// This makes the roulette produce the random noises. // This makes the roulette produce the random noises.
if ((player->kartstuff[k_itemroulette] % 3) == 1 && P_IsLocalPlayer(player)) if ((player->kartstuff[k_itemroulette] % 3) == 1 && P_IsLocalPlayer(player))
S_StartSound(NULL, sfx_mkitm1 + ((player->kartstuff[k_itemroulette] / 3) % 8)); {
#define PLAYROULETTESND S_StartSound(NULL, sfx_mkitm1 + ((player->kartstuff[k_itemroulette] / 3) % 8));
if (splitscreen)
{
if (players[displayplayer].kartstuff[k_itemroulette])
{
if (player == &players[displayplayer])
PLAYROULETTESND;
}
else if (players[secondarydisplayplayer].kartstuff[k_itemroulette])
{
if (player == &players[secondarydisplayplayer])
PLAYROULETTESND;
}
else if (players[thirddisplayplayer].kartstuff[k_itemroulette] && splitscreen > 1)
{
if (player == &players[thirddisplayplayer])
PLAYROULETTESND;
}
else if (players[fourthdisplayplayer].kartstuff[k_itemroulette] && splitscreen > 2)
{
if (player == &players[fourthdisplayplayer])
PLAYROULETTESND;
}
}
else
PLAYROULETTESND;
#undef PLAYROULETTESND
}
roulettestop = TICRATE + (3*(pingame - player->kartstuff[k_position])); roulettestop = TICRATE + (3*(pingame - player->kartstuff[k_position]));
@ -1242,7 +1271,7 @@ static void K_UpdateOffroad(player_t *player)
} }
// These have to go earlier than its sisters because of K_RespawnChecker... // These have to go earlier than its sisters because of K_RespawnChecker...
static void K_MatchGenericExtraFlags(mobj_t *mo, mobj_t *master) void K_MatchGenericExtraFlags(mobj_t *mo, mobj_t *master)
{ {
// flipping // flipping
mo->eflags = (mo->eflags & ~MFE_VERTICALFLIP)|(master->eflags & MFE_VERTICALFLIP); mo->eflags = (mo->eflags & ~MFE_VERTICALFLIP)|(master->eflags & MFE_VERTICALFLIP);
@ -5109,8 +5138,10 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
player->mo->eflags |= MFE_DRAWONLYFORP3; player->mo->eflags |= MFE_DRAWONLYFORP3;
else if (player == &players[fourthdisplayplayer] && splitscreen > 2) else if (player == &players[fourthdisplayplayer] && splitscreen > 2)
player->mo->eflags |= MFE_DRAWONLYFORP4; player->mo->eflags |= MFE_DRAWONLYFORP4;
else else if (player == &players[consoleplayer])
player->mo->eflags |= MFE_DRAWONLYFORP1; player->mo->eflags |= MFE_DRAWONLYFORP1;
else
player->mo->flags2 |= MF2_DONTDRAW;
} }
else else
player->mo->eflags &= ~(MFE_DRAWONLYFORP1|MFE_DRAWONLYFORP2|MFE_DRAWONLYFORP3|MFE_DRAWONLYFORP4); player->mo->eflags &= ~(MFE_DRAWONLYFORP1|MFE_DRAWONLYFORP2|MFE_DRAWONLYFORP3|MFE_DRAWONLYFORP4);
@ -7049,12 +7080,17 @@ static void K_drawBattleFullscreen(void)
x = BASEVIDWIDTH/4; x = BASEVIDWIDTH/4;
} }
else else
{
if (stplyr->exiting)
{ {
if (stplyr == &players[secondarydisplayplayer]) if (stplyr == &players[secondarydisplayplayer])
x = BASEVIDWIDTH-96; x = BASEVIDWIDTH-96;
else else
x = 96; x = 96;
} }
else
scale /= 2;
}
} }
if (stplyr->exiting) if (stplyr->exiting)
@ -7574,7 +7610,7 @@ void K_drawKartHUD(void)
K_drawKartMinimap(); K_drawKartMinimap();
// Draw full screen stuff that turns off the rest of the HUD // Draw full screen stuff that turns off the rest of the HUD
if (mapreset) if (mapreset && stplyr == &players[displayplayer])
{ {
K_drawChallengerScreen(); K_drawChallengerScreen();
return; return;
@ -7690,6 +7726,13 @@ void K_drawKartHUD(void)
if (cv_kartdebugcheckpoint.value) if (cv_kartdebugcheckpoint.value)
K_drawCheckpointDebugger(); K_drawCheckpointDebugger();
if (cv_kartdebugnodes.value)
{
UINT8 p;
for (p = 0; p < MAXPLAYERS; p++)
V_DrawString(8, 64+(8*p), V_YELLOWMAP, va("%d - %d", p, playernode[p]));
}
} }
//} //}

View file

@ -21,6 +21,7 @@ void K_RegisterKartStuff(void);
boolean K_IsPlayerLosing(player_t *player); boolean K_IsPlayerLosing(player_t *player);
boolean K_IsPlayerWanted(player_t *player); boolean K_IsPlayerWanted(player_t *player);
void K_KartBouncing(mobj_t *mobj1, mobj_t *mobj2, boolean bounce, boolean solid); void K_KartBouncing(mobj_t *mobj1, mobj_t *mobj2, boolean bounce, boolean solid);
void K_MatchGenericExtraFlags(mobj_t *mo, mobj_t *master);
void K_RespawnChecker(player_t *player); void K_RespawnChecker(player_t *player);
void K_KartMoveAnimation(player_t *player); void K_KartMoveAnimation(player_t *player);
void K_KartPlayerThink(player_t *player, ticcmd_t *cmd); void K_KartPlayerThink(player_t *player, ticcmd_t *cmd);

View file

@ -2094,6 +2094,19 @@ static int lib_kKartBouncing(lua_State *L)
return 0; return 0;
} }
static int lib_kMatchGenericExtraFlags(lua_State *L)
{
mobj_t *mo = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ));
mobj_t *master = *((mobj_t **)luaL_checkudata(L, 2, META_MOBJ));
NOHUD
if (!mo)
return LUA_ErrInvalid(L, "mobj_t");
if (!master)
return LUA_ErrInvalid(L, "mobj_t");
K_MatchGenericExtraFlags(mo, master);
return 0;
}
static int lib_kDoInstashield(lua_State *L) static int lib_kDoInstashield(lua_State *L)
{ {
player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER)); player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER));
@ -2490,6 +2503,7 @@ static luaL_Reg lib[] = {
{"K_IsPlayerLosing",lib_kIsPlayerLosing}, {"K_IsPlayerLosing",lib_kIsPlayerLosing},
{"K_IsPlayerWanted",lib_kIsPlayerWanted}, {"K_IsPlayerWanted",lib_kIsPlayerWanted},
{"K_KartBouncing",lib_kKartBouncing}, {"K_KartBouncing",lib_kKartBouncing},
{"K_MatchGenericExtraFlags",lib_kMatchGenericExtraFlags},
{"K_DoInstashield",lib_kDoInstashield}, {"K_DoInstashield",lib_kDoInstashield},
{"K_SpinPlayer",lib_kSpinPlayer}, {"K_SpinPlayer",lib_kSpinPlayer},
{"K_SquishPlayer",lib_kSquishPlayer}, {"K_SquishPlayer",lib_kSquishPlayer},

File diff suppressed because it is too large Load diff

View file

@ -6792,22 +6792,18 @@ void P_MobjThinker(mobj_t *mobj)
if (mobj->target && mobj->target->health if (mobj->target && mobj->target->health
&& mobj->target->player && !mobj->target->player->spectator && mobj->target->player && !mobj->target->player->spectator
&& mobj->target->player->health && mobj->target->player->playerstate != PST_DEAD && mobj->target->player->health && mobj->target->player->playerstate != PST_DEAD
&& players[displayplayer].mo && !players[displayplayer].spectator) /*&& players[displayplayer].mo && !players[displayplayer].spectator*/)
{ {
fixed_t scale = mobj->target->scale; fixed_t scale = mobj->target->scale;
mobj->color = mobj->target->color; mobj->color = mobj->target->color;
K_MatchGenericExtraFlags(mobj, mobj->target);
if (G_RaceGametype() if ((G_RaceGametype() || mobj->target->player->kartstuff[k_bumper] <= 0)
|| mobj->target->player == &players[displayplayer]
|| mobj->target->player->kartstuff[k_bumper] <= 0
|| (mobj->target->player->mo->flags2 & MF2_DONTDRAW)
#if 1 // Set to 0 to test without needing to host #if 1 // Set to 0 to test without needing to host
|| !netgame || ((mobj->target->player == &players[displayplayer]) || P_IsLocalPlayer(mobj->target->player))
#endif #endif
) )
mobj->flags2 |= MF2_DONTDRAW; mobj->flags2 |= MF2_DONTDRAW;
else
mobj->flags2 &= ~MF2_DONTDRAW;
P_UnsetThingPosition(mobj); P_UnsetThingPosition(mobj);
mobj->x = mobj->target->x; mobj->x = mobj->target->x;
@ -6827,10 +6823,13 @@ void P_MobjThinker(mobj_t *mobj)
} }
P_SetThingPosition(mobj); P_SetThingPosition(mobj);
if (!splitscreen)
{
scale += FixedMul(FixedDiv(abs(P_AproxDistance(players[displayplayer].mo->x-mobj->target->x, scale += FixedMul(FixedDiv(abs(P_AproxDistance(players[displayplayer].mo->x-mobj->target->x,
players[displayplayer].mo->y-mobj->target->y)), RING_DIST), mobj->target->scale); players[displayplayer].mo->y-mobj->target->y)), RING_DIST), mobj->target->scale);
if (scale > 16*FRACUNIT) if (scale > 16*FRACUNIT)
scale = 16*FRACUNIT; scale = 16*FRACUNIT;
}
mobj->destscale = scale; mobj->destscale = scale;
if (!mobj->tracer) if (!mobj->tracer)
@ -10122,6 +10121,8 @@ void P_SpawnPlayer(INT32 playernum)
continue; continue;
if (!playeringame[i] || players[i].spectator) if (!playeringame[i] || players[i].spectator)
continue; continue;
if (players[i].jointime <= 1) // Prevent splitscreen hosters/joiners from only adding 1 player at a time in empty servers
continue;
pcount++; pcount++;
} }

View file

@ -1140,35 +1140,58 @@ boolean P_EndingMusic(player_t *player)
{ {
char buffer[9]; char buffer[9];
boolean looping = true; boolean looping = true;
INT32 bestlocalpos;
player_t *bestlocalplayer;
if (!P_IsLocalPlayer(player)) // Only applies to a local player if (!P_IsLocalPlayer(player)) // Only applies to a local player
return false; return false;
// Event - Level Finish // Event - Level Finish
if (splitscreen // Check for if this is valid or not
&& (players[displayplayer].exiting if (splitscreen)
|| players[secondarydisplayplayer].exiting
|| ((splitscreen < 2) && players[thirddisplayplayer].exiting)
|| ((splitscreen < 3) && players[fourthdisplayplayer].exiting)))
{ {
sprintf(buffer, "k*ok"); if (!((players[displayplayer].exiting || (players[displayplayer].pflags & PF_TIMEOVER))
} || (players[secondarydisplayplayer].exiting || (players[secondarydisplayplayer].pflags & PF_TIMEOVER))
else if (player->pflags & PF_TIMEOVER) // || !player->lives) -- outta lives, outta time || ((splitscreen < 2) && (players[thirddisplayplayer].exiting || (players[thirddisplayplayer].pflags & PF_TIMEOVER)))
{ || ((splitscreen < 3) && (players[fourthdisplayplayer].exiting || (players[fourthdisplayplayer].pflags & PF_TIMEOVER)))))
sprintf(buffer, "k*lose");
}
else if (player->exiting)
{
if (player->kartstuff[k_position] == 1)
sprintf(buffer, "k*win");
else if (K_IsPlayerLosing(player))
sprintf(buffer, "k*lose");
else
sprintf(buffer, "k*ok");
}
else
return false; return false;
bestlocalplayer = &players[displayplayer];
bestlocalpos = ((players[displayplayer].pflags & PF_TIMEOVER) ? MAXPLAYERS+1 : players[displayplayer].kartstuff[k_position]);
#define setbests(p) \
if (((players[p].pflags & PF_TIMEOVER) ? MAXPLAYERS+1 : players[p].kartstuff[k_position]) < bestlocalpos) \
{ \
bestlocalplayer = &players[p]; \
bestlocalpos = ((players[p].pflags & PF_TIMEOVER) ? MAXPLAYERS+1 : players[p].kartstuff[k_position]); \
}
setbests(secondarydisplayplayer);
if (splitscreen > 1)
setbests(thirddisplayplayer);
if (splitscreen > 2)
setbests(fourthdisplayplayer);
#undef setbests
}
else
{
if (!(player->exiting || (player->pflags & PF_TIMEOVER)))
return false;
bestlocalplayer = player;
bestlocalpos = ((player->pflags & PF_TIMEOVER) ? MAXPLAYERS+1 : player->kartstuff[k_position]);
}
if (G_RaceGametype() && bestlocalpos == MAXPLAYERS+1)
sprintf(buffer, "k*lose"); // krfail, for eventual F-Zero death results theme
else
{
if (bestlocalpos == 1)
sprintf(buffer, "k*win");
else if (K_IsPlayerLosing(bestlocalplayer))
sprintf(buffer, "k*lose");
else
sprintf(buffer, "k*ok");
}
S_SpeedMusic(1.0f); S_SpeedMusic(1.0f);
if (G_RaceGametype()) if (G_RaceGametype())
@ -8931,7 +8954,7 @@ void P_PlayerThink(player_t *player)
} }
#ifdef SEENAMES #ifdef SEENAMES
if (netgame && player == &players[displayplayer] && !(leveltime % (TICRATE/5))) if (netgame && player == &players[displayplayer] && !(leveltime % (TICRATE/5)) && !splitscreen)
{ {
seenplayer = NULL; seenplayer = NULL;
@ -9145,7 +9168,7 @@ void P_PlayerThink(player_t *player)
} }
} }
if ((netgame || splitscreen) && player->spectator && cmd->buttons & BT_ATTACK && !player->powers[pw_flashing]) if ((netgame || multiplayer) && player->spectator && cmd->buttons & BT_ATTACK && !player->powers[pw_flashing])
{ {
player->pflags ^= PF_WANTSTOJOIN; player->pflags ^= PF_WANTSTOJOIN;
player->powers[pw_flashing] = TICRATE/2 + 1; player->powers[pw_flashing] = TICRATE/2 + 1;

View file

@ -185,16 +185,6 @@ void SplitScreen_OnChange(void)
{ {
UINT8 i; UINT8 i;
if (!cv_debug && netgame)
{
if (splitscreen)
{
CONS_Alert(CONS_NOTICE, M_GetText("Splitscreen not supported in netplay, sorry!\n"));
splitscreen = 0;
}
return;
}
// recompute screen size // recompute screen size
R_ExecuteSetViewSize(); R_ExecuteSetViewSize();

View file

@ -1927,7 +1927,7 @@ static void ST_overlayDrawer(void)
) )
ST_drawLevelTitle(); ST_drawLevelTitle();
if (!hu_showscores && !splitscreen && netgame && displayplayer == consoleplayer && !mapreset) if (!hu_showscores && netgame && !mapreset)
{ {
/*if (G_GametypeUsesLives() && stplyr->lives <= 0 && countdown != 1) /*if (G_GametypeUsesLives() && stplyr->lives <= 0 && countdown != 1)
V_DrawCenteredString(BASEVIDWIDTH/2, STRINGY(132), 0, M_GetText("Press F12 to watch another player.")); V_DrawCenteredString(BASEVIDWIDTH/2, STRINGY(132), 0, M_GetText("Press F12 to watch another player."));
@ -1952,6 +1952,21 @@ static void ST_overlayDrawer(void)
) )
{ {
// SRB2kart: changed positions & text // SRB2kart: changed positions & text
if (splitscreen)
{
INT32 splitflags = K_calcSplitFlags(0);
V_DrawThinString(2, (BASEVIDHEIGHT/2)-20, V_YELLOWMAP|V_HUDTRANSHALF|splitflags, M_GetText("- SPECTATING -"));
if (stplyr->powers[pw_flashing])
V_DrawString(2, (BASEVIDHEIGHT/2)-10, V_HUDTRANSHALF|splitflags, M_GetText("Item - . . ."));
else if (stplyr->pflags & PF_WANTSTOJOIN)
V_DrawThinString(2, (BASEVIDHEIGHT/2)-10, V_HUDTRANSHALF|splitflags, M_GetText("Item - Cancel Join"));
/*else if (G_GametypeHasTeams())
V_DrawThinString(2, (BASEVIDHEIGHT/2)-10, V_HUDTRANSHALF|splitflags, M_GetText("Item - Join Team"));*/
else
V_DrawThinString(2, (BASEVIDHEIGHT/2)-10, V_HUDTRANSHALF|splitflags, M_GetText("Item - Join Game"));
}
else
{
V_DrawString(2, BASEVIDHEIGHT-40, V_HUDTRANSHALF|V_YELLOWMAP, M_GetText("- SPECTATING -")); V_DrawString(2, BASEVIDHEIGHT-40, V_HUDTRANSHALF|V_YELLOWMAP, M_GetText("- SPECTATING -"));
if (stplyr->powers[pw_flashing]) if (stplyr->powers[pw_flashing])
V_DrawString(2, BASEVIDHEIGHT-30, V_HUDTRANSHALF, M_GetText("Item - . . .")); V_DrawString(2, BASEVIDHEIGHT-30, V_HUDTRANSHALF, M_GetText("Item - . . ."));
@ -1965,6 +1980,7 @@ static void ST_overlayDrawer(void)
V_DrawString(2, BASEVIDHEIGHT-10, V_HUDTRANSHALF, M_GetText("Brake - Sink")); V_DrawString(2, BASEVIDHEIGHT-10, V_HUDTRANSHALF, M_GetText("Brake - Sink"));
} }
} }
}
ST_drawDebugInfo(); ST_drawDebugInfo();
} }