Merge branch 'master' of https://git.do.srb2.org/KartKrew/Kart into servers-16

# Conflicts:
#	src/d_main.c
This commit is contained in:
toaster 2022-11-21 15:29:35 +00:00
commit 62bf8458c5
41 changed files with 1785 additions and 953 deletions

View file

@ -531,8 +531,10 @@ typedef enum
CL_SEARCHING,
CL_CHECKFILES,
CL_DOWNLOADFILES,
CL_DOWNLOADFAILED,
CL_ASKJOIN,
CL_LOADFILES,
CL_SETUPFILES,
CL_WAITJOINRESPONSE,
CL_DOWNLOADSAVEGAME,
CL_CONNECTED,
@ -615,8 +617,12 @@ static inline void CL_DrawConnectionStatus(void)
break;
case CL_ASKFULLFILELIST:
case CL_CONFIRMCONNECT:
case CL_DOWNLOADFAILED:
cltext = "";
break;
case CL_SETUPFILES:
cltext = M_GetText("Configuring addons...");
break;
case CL_ASKJOIN:
case CL_WAITJOINRESPONSE:
if (serverisfull)
@ -655,8 +661,8 @@ static inline void CL_DrawConnectionStatus(void)
V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-24-32, V_YELLOWMAP, "Checking server addons...");
totalfileslength = (INT32)((checkednum/(double)(fileneedednum)) * 256);
M_DrawTextBox(BASEVIDWIDTH/2-128-8, BASEVIDHEIGHT-24-8, 32, 1);
V_DrawFill(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-24, 256, 8, 175);
V_DrawFill(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-24, totalfileslength, 8, 160);
V_DrawFill(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-24, 256, 8, 111);
V_DrawFill(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-24, totalfileslength, 8, 96);
V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-24, V_20TRANS|V_MONOSPACE,
va(" %2u/%2u Files",checkednum,fileneedednum));
}
@ -677,8 +683,8 @@ static inline void CL_DrawConnectionStatus(void)
V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-24-32, V_YELLOWMAP, "Loading server addons...");
totalfileslength = (INT32)((loadcompletednum/(double)(fileneedednum)) * 256);
M_DrawTextBox(BASEVIDWIDTH/2-128-8, BASEVIDHEIGHT-24-8, 32, 1);
V_DrawFill(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-24, 256, 8, 175);
V_DrawFill(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-24, totalfileslength, 8, 160);
V_DrawFill(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-24, 256, 8, 111);
V_DrawFill(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-24, totalfileslength, 8, 96);
V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-24, V_20TRANS|V_MONOSPACE,
va(" %2u/%2u Files",loadcompletednum,fileneedednum));
}
@ -719,8 +725,10 @@ static inline void CL_DrawConnectionStatus(void)
strncpy(tempname, filename, sizeof(tempname)-1);
}
V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-58-30, 0,
va(M_GetText("%s downloading"), ((cl_mode == CL_DOWNLOADHTTPFILES) ? "\x82""HTTP" : "\x85""Direct")));
V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-58-22, V_YELLOWMAP,
va(M_GetText("Downloading \"%s\""), tempname));
va(M_GetText("\"%s\""), tempname));
V_DrawString(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-58, V_20TRANS|V_MONOSPACE,
va(" %4uK/%4uK",fileneeded[lastfilenum].currentsize>>10,file->totalsize>>10));
V_DrawRightAlignedString(BASEVIDWIDTH/2+128, BASEVIDHEIGHT-58, V_20TRANS|V_MONOSPACE,
@ -736,8 +744,8 @@ static inline void CL_DrawConnectionStatus(void)
V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-24-14, V_YELLOWMAP, "Overall Download Progress");
totalfileslength = (INT32)((totaldldsize/(double)totalfilesrequestedsize) * 256);
M_DrawTextBox(BASEVIDWIDTH/2-128-8, BASEVIDHEIGHT-24-8, 32, 1);
V_DrawFill(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-24, 256, 8, 175);
V_DrawFill(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-24, totalfileslength, 8, 160);
V_DrawFill(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-24, 256, 8, 111);
V_DrawFill(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-24, totalfileslength, 8, 96);
if (totalfilesrequestedsize>>20 >= 10) //display in MB if over 10MB
V_DrawString(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-24, V_20TRANS|V_MONOSPACE,
@ -1497,6 +1505,10 @@ static void M_ConfirmConnect(void)
{
cl_mode = CL_DOWNLOADFILES;
}
else
{
cl_mode = CL_DOWNLOADFAILED;
}
}
#ifdef HAVE_CURL
else
@ -1645,6 +1657,10 @@ static boolean CL_FinishedFileList(void)
{
cl_mode = CL_DOWNLOADFILES;
}
else
{
cl_mode = CL_DOWNLOADFAILED;
}
}
#endif
}
@ -1850,8 +1866,28 @@ static boolean CL_ServerConnectionTicker(const char *tmpsave, tic_t *oldtic, tic
cl_mode = CL_LOADFILES;
break;
case CL_DOWNLOADFAILED:
{
CONS_Printf(M_GetText("Legacy downloader request packet failed.\n"));
CONS_Printf(M_GetText("Network game synchronization aborted.\n"));
D_QuitNetGame();
CL_Reset();
D_StartTitle();
M_StartMessage(M_GetText(
"The direct download encountered an error.\n"
"See the logfile for more info.\n"
"\n"
"Press (B)\n"
), NULL, MM_NOTHING);
return false;
}
case CL_LOADFILES:
if (CL_LoadServerFiles())
if (CL_LoadServerFiles())
cl_mode = CL_SETUPFILES;
break;
case CL_SETUPFILES:
if (P_PartialAddGetStage() < 0 || P_MultiSetupWadFiles(false))
{
*asksent = 0; //This ensure the first join ask is right away
firstconnectattempttime = I_GetTime();
@ -2079,7 +2115,12 @@ static void CL_ConnectToServer(void)
{
// If the connection was aborted for some reason, leave
if (!CL_ServerConnectionTicker(tmpsave, &oldtic, &asksent))
{
if (P_PartialAddGetStage() >= 0)
P_MultiSetupWadFiles(true); // in case any partial adds were done
return;
}
if (server)
{
@ -2487,6 +2528,11 @@ void CL_ClearPlayer(INT32 playernum)
{
int i;
if (players[playernum].follower)
{
K_RemoveFollower(&players[playernum]);
}
if (players[playernum].mo)
{
P_RemoveMobj(players[playernum].mo);

View file

@ -993,6 +993,7 @@ void D_StartTitle(void)
memset(deviceResponding, false, sizeof (deviceResponding));
F_StartTitleScreen();
M_ClearMenus(false);
// Reset the palette
if (rendermode != render_none)
@ -1202,9 +1203,6 @@ D_ConvertVersionNumbers (void)
void D_SRB2Main(void)
{
INT32 i, p;
INT32 numbasemapheaders;
INT32 pstartmap = 0;
boolean autostart = false;
@ -1461,9 +1459,7 @@ void D_SRB2Main(void)
//
// search for mainwad maps
//
P_InitMapData(0);
numbasemapheaders = nummapheaders;
P_InitMapData(false);
CON_SetLoadingProgress(LOADED_IWAD);
@ -1474,7 +1470,7 @@ void D_SRB2Main(void)
//
// search for pwad maps
//
P_InitMapData(numbasemapheaders);
P_InitMapData(true);
CON_SetLoadingProgress(LOADED_PWAD);

View file

@ -295,10 +295,10 @@ consvar_t cv_follower[MAXSPLITSCREENPLAYERS] = {
// player's follower colors... Also saved...
consvar_t cv_followercolor[MAXSPLITSCREENPLAYERS] = {
CVAR_INIT ("followercolor", "1", CV_SAVE|CV_CALL|CV_NOINIT, Followercolor_cons_t, Followercolor_OnChange),
CVAR_INIT ("followercolor2", "1", CV_SAVE|CV_CALL|CV_NOINIT, Followercolor_cons_t, Followercolor2_OnChange),
CVAR_INIT ("followercolor3", "1", CV_SAVE|CV_CALL|CV_NOINIT, Followercolor_cons_t, Followercolor3_OnChange),
CVAR_INIT ("followercolor4", "1", CV_SAVE|CV_CALL|CV_NOINIT, Followercolor_cons_t, Followercolor4_OnChange)
CVAR_INIT ("followercolor", "Match", CV_SAVE|CV_CALL|CV_NOINIT, Followercolor_cons_t, Followercolor_OnChange),
CVAR_INIT ("followercolor2", "Match", CV_SAVE|CV_CALL|CV_NOINIT, Followercolor_cons_t, Followercolor2_OnChange),
CVAR_INIT ("followercolor3", "Match", CV_SAVE|CV_CALL|CV_NOINIT, Followercolor_cons_t, Followercolor3_OnChange),
CVAR_INIT ("followercolor4", "Match", CV_SAVE|CV_CALL|CV_NOINIT, Followercolor_cons_t, Followercolor4_OnChange)
};
// last selected profile, unaccessible cvar only set internally but is saved.
@ -386,7 +386,6 @@ consvar_t cv_gardentop = CVAR_INIT ("gardentop", "On", CV_NETVAR, CV_OnOff,
consvar_t cv_dualsneaker = CVAR_INIT ("dualsneaker", "On", CV_NETVAR, CV_OnOff, NULL);
consvar_t cv_triplesneaker = CVAR_INIT ("triplesneaker", "On", CV_NETVAR, CV_OnOff, NULL);
consvar_t cv_triplebanana = CVAR_INIT ("triplebanana", "On", CV_NETVAR, CV_OnOff, NULL);
consvar_t cv_decabanana = CVAR_INIT ("decabanana", "On", CV_NETVAR, CV_OnOff, NULL);
consvar_t cv_tripleorbinaut = CVAR_INIT ("tripleorbinaut", "On", CV_NETVAR, CV_OnOff, NULL);
consvar_t cv_quadorbinaut = CVAR_INIT ("quadorbinaut", "On", CV_NETVAR, CV_OnOff, NULL);
consvar_t cv_dualjawz = CVAR_INIT ("dualjawz", "On", CV_NETVAR, CV_OnOff, NULL);
@ -846,27 +845,6 @@ void D_RegisterClientCommands(void)
{
INT32 i;
for (i = 0; i < MAXSKINCOLORS; i++)
{
Color_cons_t[i].value = i;
Color_cons_t[i].strvalue = skincolors[i].name;
}
for (i = 2; i < MAXSKINCOLORS; i++)
{
Followercolor_cons_t[i].value = i-2;
Followercolor_cons_t[i].strvalue = skincolors[i-2].name;
}
Followercolor_cons_t[1].value = FOLLOWERCOLOR_MATCH;
Followercolor_cons_t[1].strvalue = "Match"; // Add "Match" option, which will make the follower color match the player's
Followercolor_cons_t[0].value = FOLLOWERCOLOR_OPPOSITE;
Followercolor_cons_t[0].strvalue = "Opposite"; // Add "Opposite" option, ...which is like "Match", but for coloropposite.
Color_cons_t[MAXSKINCOLORS].value = Followercolor_cons_t[MAXSKINCOLORS+2].value = 0;
Color_cons_t[MAXSKINCOLORS].strvalue = Followercolor_cons_t[MAXSKINCOLORS+2].strvalue = NULL;
// Set default player names
// Monster Iestyn (12/08/19): not sure where else I could have actually put this, but oh well
for (i = 0; i < MAXPLAYERS; i++)
@ -1494,7 +1472,7 @@ static void SendNameAndColor(UINT8 n)
const INT32 playernum = g_localplayers[n];
player_t *player = &players[playernum];
char buf[MAXPLAYERNAME+9];
char buf[MAXPLAYERNAME+12];
char *p;
if (splitscreen < n)
@ -1525,14 +1503,11 @@ static void SendNameAndColor(UINT8 n)
if (!cv_followercolor[n].value)
CV_StealthSet(&cv_followercolor[n], "Match"); // set it to "Match". I don't care about your stupidity!
// so like, this is sent before we even use anything like cvars or w/e so it's possible that follower is set to a pretty yikes value, so let's fix that before we send garbage that could crash the game:
if (cv_follower[n].value >= numfollowers || cv_follower[n].value < -1)
CV_StealthSet(&cv_follower[n], "-1");
if (!strcmp(cv_playername[n].string, player_names[playernum])
&& cv_playercolor[n].value == player->skincolor
&& !strcmp(cv_skin[n].string, skins[player->skin].name)
&& cv_follower[n].value == player->followerskin
&& !stricmp(cv_skin[n].string, skins[player->skin].name)
&& !stricmp(cv_follower[n].string,
(player->followerskin < 0 ? "None" : followers[player->followerskin].name))
&& cv_followercolor[n].value == player->followercolor)
return;
@ -1554,31 +1529,28 @@ static void SendNameAndColor(UINT8 n)
K_KartResetPlayerColor(player);
// Update follower for local games:
if (cv_follower[n].value >= -1 && cv_follower[n].value != player->followerskin)
K_SetFollowerByNum(playernum, cv_follower[n].value);
player->followercolor = cv_followercolor[n].value;
if (metalrecording && n == 0)
{ // Starring Metal Sonic as themselves, obviously.
SetPlayerSkinByNum(playernum, 5);
CV_StealthSet(&cv_skin[n], skins[5].name);
}
else if ((foundskin = R_SkinAvailable(cv_skin[n].string)) != -1 && R_SkinUsable(playernum, foundskin))
if ((foundskin = R_SkinAvailable(cv_skin[n].string)) != -1 && R_SkinUsable(playernum, foundskin))
{
cv_skin[n].value = foundskin;
SetPlayerSkin(playernum, cv_skin[n].string);
CV_StealthSet(&cv_skin[n], skins[cv_skin[n].value].name);
CV_StealthSet(&cv_skin[n], skins[foundskin].name);
cv_skin[n].value = foundskin;
}
else
{
cv_skin[n].value = players[playernum].skin;
CV_StealthSet(&cv_skin[n], skins[player->skin].name);
cv_skin[n].value = player->skin;
// will always be same as current
SetPlayerSkin(playernum, cv_skin[n].string);
}
player->followercolor = cv_followercolor[n].value;
// Update follower for local games:
foundskin = K_FollowerAvailable(cv_follower[n].string);
CV_StealthSet(&cv_follower[n], (foundskin == -1) ? "None" : followers[foundskin].name);
cv_follower[n].value = foundskin;
K_SetFollowerByNum(playernum, foundskin);
return;
}
@ -1608,12 +1580,20 @@ static void SendNameAndColor(UINT8 n)
cv_skin[n].value = 0;
}
cv_follower[n].value = K_FollowerAvailable(cv_follower[n].string);
if (cv_follower[n].value < 0)
{
CV_StealthSet(&cv_follower[n], "None");
cv_follower[n].value = -1;
}
// Finally write out the complete packet and send it off.
WRITESTRINGN(p, cv_playername[n].zstring, MAXPLAYERNAME);
WRITEUINT32(p, (UINT32)player->availabilities);
WRITEUINT16(p, (UINT16)cv_playercolor[n].value);
WRITEUINT8(p, (UINT8)cv_skin[n].value);
WRITESINT8(p, (SINT8)cv_follower[n].value);
WRITEINT16(p, (INT16)cv_follower[n].value);
//CONS_Printf("Sending follower id %d\n", (INT16)cv_follower[n].value);
WRITEUINT16(p, (UINT16)cv_followercolor[n].value);
SendNetXCmdForPlayer(n, XD_NAMEANDCOLOR, buf, p - buf);
@ -1625,7 +1605,7 @@ static void Got_NameAndColor(UINT8 **cp, INT32 playernum)
char name[MAXPLAYERNAME+1];
UINT16 color, followercolor;
UINT8 skin;
SINT8 follower;
INT16 follower;
SINT8 localplayer = -1;
UINT8 i;
@ -1654,7 +1634,8 @@ static void Got_NameAndColor(UINT8 **cp, INT32 playernum)
p->availabilities = READUINT32(*cp);
color = READUINT16(*cp);
skin = READUINT8(*cp);
follower = READSINT8(*cp);
follower = READINT16(*cp);
//CONS_Printf("Recieved follower id %d\n", follower);
followercolor = READUINT16(*cp);
// set name
@ -6080,6 +6061,7 @@ static void Name_OnChange(void)
{
CONS_Alert(CONS_NOTICE, M_GetText("You may not change your name when chat is muted.\n"));
CV_StealthSet(&cv_playername[0], player_names[consoleplayer]);
return;
}
else
SendNameAndColor(0);
@ -6120,207 +6102,56 @@ static void Name4_OnChange(void)
}
// sends the follower change for players
static void Follower_OnChange(void)
static void FollowerAny_OnChange(UINT8 pnum)
{
char str[SKINNAMESIZE+1], cpy[SKINNAMESIZE+1];
INT32 num;
// there is a slight chance that we will actually use a string instead so...
// let's investigate the string...
strcpy(str, cv_follower[0].string);
strcpy(cpy, cv_follower[0].string);
strlwr(str);
if (stricmp(cpy,"0") !=0 && !atoi(cpy)) // yep, that's a string alright...
{
if (stricmp(cpy, "None") == 0)
{
CV_StealthSet(&cv_follower[0], "-1");
if (!Playing())
return; // don't send anything there.
SendNameAndColor(0);
return;
}
num = K_FollowerAvailable(str);
if (num == -1) // that's an error.
CONS_Alert(CONS_WARNING, M_GetText("Follower '%s' not found\n"), str);
CV_StealthSet(&cv_follower[0], str);
cv_follower[0].value = num;
}
if (!Playing())
return; // don't send anything there.
SendNameAndColor(0);
SendNameAndColor(pnum);
}
// sends the follower change for players
static void Follower_OnChange(void)
{
FollowerAny_OnChange(0);
}
// About the same as Color_OnChange but for followers.
static void Followercolor_OnChange(void)
{
if (!Playing())
return; // do whatever you want if you aren't in the game or don't have a follower.
if (!P_PlayerMoving(consoleplayer))
{
// Color change menu scrolling fix is no longer necessary
SendNameAndColor(0);
}
FollowerAny_OnChange(0);
}
// repeat for the 3 other players
static void Follower2_OnChange(void)
{
char str[SKINNAMESIZE+1], cpy[SKINNAMESIZE+1];
INT32 num;
// there is a slight chance that we will actually use a string instead so...
// let's investigate the string...
strcpy(str, cv_follower[1].string);
strcpy(cpy, cv_follower[1].string);
strlwr(str);
if (stricmp(cpy,"0") !=0 && !atoi(cpy)) // yep, that's a string alright...
{
if (stricmp(cpy, "None") == 0)
{
CV_StealthSet(&cv_follower[1], "-1");
if (!Playing())
return; // don't send anything there.
SendNameAndColor(1);
return;
}
num = K_FollowerAvailable(str);
if (num == -1) // that's an error.
CONS_Alert(CONS_WARNING, M_GetText("Follower '%s' not found\n"), str);
CV_StealthSet(&cv_follower[1], str);
cv_follower[1].value = num;
}
if (!Playing())
return; // don't send anything there.
SendNameAndColor(1);
FollowerAny_OnChange(1);
}
static void Followercolor2_OnChange(void)
{
if (!Playing())
return; // do whatever you want if you aren't in the game or don't have a follower.
if (!P_PlayerMoving(g_localplayers[1]))
{
// Color change menu scrolling fix is no longer necessary
SendNameAndColor(1);
}
FollowerAny_OnChange(1);
}
static void Follower3_OnChange(void)
{
char str[SKINNAMESIZE+1], cpy[SKINNAMESIZE+1];
INT32 num;
// there is a slight chance that we will actually use a string instead so...
// let's investigate the string...
strcpy(str, cv_follower[2].string);
strcpy(cpy, cv_follower[2].string);
strlwr(str);
if (stricmp(cpy,"0") !=0 && !atoi(cpy)) // yep, that's a string alright...
{
if (stricmp(cpy, "None") == 0)
{
CV_StealthSet(&cv_follower[2], "-1");
if (!Playing())
return; // don't send anything there.
SendNameAndColor(2);
return;
}
num = K_FollowerAvailable(str);
if (num == -1) // that's an error.
CONS_Alert(CONS_WARNING, M_GetText("Follower '%s' not found\n"), str);
CV_StealthSet(&cv_follower[2], str);
cv_follower[2].value = num;
}
if (!Playing())
return; // don't send anything there.
SendNameAndColor(2);
FollowerAny_OnChange(2);
}
static void Followercolor3_OnChange(void)
{
if (!Playing())
return; // do whatever you want if you aren't in the game or don't have a follower.
if (!P_PlayerMoving(g_localplayers[2]))
{
// Color change menu scrolling fix is no longer necessary
SendNameAndColor(2);
}
FollowerAny_OnChange(2);
}
static void Follower4_OnChange(void)
{
char str[SKINNAMESIZE+1], cpy[SKINNAMESIZE+1];
INT32 num;
// there is a slight chance that we will actually use a string instead so...
// let's investigate the string...
strcpy(str, cv_follower[3].string);
strcpy(cpy, cv_follower[3].string);
strlwr(str);
if (stricmp(cpy,"0") !=0 && !atoi(cpy)) // yep, that's a string alright...
{
if (stricmp(cpy, "None") == 0)
{
CV_StealthSet(&cv_follower[3], "-1");
if (!Playing())
return; // don't send anything there.
SendNameAndColor(3);
return;
}
num = K_FollowerAvailable(str);
if (num == -1) // that's an error.
CONS_Alert(CONS_WARNING, M_GetText("Follower '%s' not found\n"), str);
CV_StealthSet(&cv_follower[3], str);
cv_follower[3].value = num;
}
if (!Playing())
return; // don't send anything there.
SendNameAndColor(3);
FollowerAny_OnChange(3);
}
static void Followercolor4_OnChange(void)
{
if (!Playing())
return; // do whatever you want if you aren't in the game or don't have a follower.
if (!P_PlayerMoving(g_localplayers[3]))
{
// Color change menu scrolling fix is no longer necessary
SendNameAndColor(3);
}
FollowerAny_OnChange(3);
}
/** Sends a skin change for the console player, unless that player is moving. Also forces them to spectate if the change is done during gameplay
@ -6340,7 +6171,9 @@ static void Skin_OnChange(void)
}
if (CanChangeSkinWhilePlaying(consoleplayer))
{
SendNameAndColor(0);
}
else
{
CONS_Alert(CONS_NOTICE, M_GetText("You can't change your skin at the moment.\n"));

View file

@ -100,7 +100,6 @@ extern consvar_t
cv_dualsneaker,
cv_triplesneaker,
cv_triplebanana,
cv_decabanana,
cv_tripleorbinaut,
cv_quadorbinaut,
cv_dualjawz;

View file

@ -354,6 +354,9 @@ void CL_AbortDownloadResume(void)
pauseddownload = NULL;
}
// The following was written and, against all odds, works.
#define MORELEGACYDOWNLOADER
/** Sends requests for files in the ::fileneeded table with a status of
* ::FS_NOTFOUND.
*
@ -366,42 +369,135 @@ boolean CL_SendFileRequest(void)
char *p;
INT32 i;
INT64 totalfreespaceneeded = 0, availablefreespace;
INT32 skippedafile = -1;
#ifdef MORELEGACYDOWNLOADER
boolean firstloop = true;
#endif
#ifdef PARANOIA
if (M_CheckParm("-nodownload"))
I_Error("Attempted to download files in -nodownload mode");
{
CONS_Printf("Direct download - Attempted to download files in -nodownload mode");
return false;
}
#endif
for (i = 0; i < fileneedednum; i++)
{
#ifdef PARANOIA
if (fileneeded[i].status != FS_FOUND && fileneeded[i].status != FS_OPEN
&& (fileneeded[i].willsend == 0 || fileneeded[i].willsend == 2))
{
I_Error("Attempted to download files that were not sendable");
CONS_Printf("Direct download - attempted to download files that were not sendable\n");
return false;
}
#endif
if ((fileneeded[i].status == FS_NOTFOUND || fileneeded[i].status == FS_MD5SUMBAD || fileneeded[i].status == FS_FALLBACK))
{
// Error check for the first time around.
totalfreespaceneeded += fileneeded[i].totalsize;
}
}
I_GetDiskFreeSpace(&availablefreespace);
if (totalfreespaceneeded > availablefreespace)
{
CONS_Printf("Direct download -\n"
" To play on this server you must download %s KB,\n"
" but you have only %s KB free space on this drive\n",
sizeu1((size_t)(totalfreespaceneeded>>10)), sizeu2((size_t)(availablefreespace>>10)));
return false;
}
#ifdef MORELEGACYDOWNLOADER
tryagain:
skippedafile = -1;
#endif
#ifdef VERBOSEREQUESTFILE
CONS_Printf("Preparing packet\n");
#endif
netbuffer->packettype = PT_REQUESTFILE;
p = (char *)netbuffer->u.textcmd;
for (i = 0; i < fileneedednum; i++)
{
if ((fileneeded[i].status == FS_NOTFOUND || fileneeded[i].status == FS_MD5SUMBAD || fileneeded[i].status == FS_FALLBACK))
{
totalfreespaceneeded += fileneeded[i].totalsize;
// Pre-prepare.
size_t checklen;
nameonly(fileneeded[i].filename);
// Figure out if we'd overrun our buffer.
checklen = strlen(fileneeded[i].filename)+2; // plus the fileid (and terminator, in case this is last)
if ((UINT8 *)(p + checklen) >= netbuffer->u.textcmd + MAXTEXTCMD)
{
skippedafile = i;
// we might have a shorter file that can fit in the remaining space, and file ID permits out-of-order data
continue;
}
// Now write.
WRITEUINT8(p, i); // fileid
WRITESTRINGN(p, fileneeded[i].filename, MAX_WADPATH);
#ifdef VERBOSEREQUESTFILE
CONS_Printf(" file \"%s\" (id %d)\n", i, fileneeded[i].filename);
#endif
// put it in download dir
strcatbf(fileneeded[i].filename, downloaddir, "/");
fileneeded[i].status = FS_REQUESTED;
}
WRITEUINT8(p, 0xFF);
I_GetDiskFreeSpace(&availablefreespace);
if (totalfreespaceneeded > availablefreespace)
I_Error("To play on this server you must download %s KB,\n"
"but you have only %s KB free space on this drive\n",
sizeu1((size_t)(totalfreespaceneeded>>10)), sizeu2((size_t)(availablefreespace>>10)));
}
// prepare to download
I_mkdir(downloaddir, 0755);
return HSendPacket(servernode, true, 0, p - (char *)netbuffer->u.textcmd);
#ifdef MORELEGACYDOWNLOADER
if (firstloop)
#else
// If we're not trying extralong legacy download requests, gotta bail.
if (skippedafile != -1)
{
CONS_Printf("Direct download - missing files are as follows:\n");
for (i = 0; i < fileneedednum; i++)
{
if ((fileneeded[i].status == FS_NOTFOUND || fileneeded[i].status == FS_MD5SUMBAD || fileneeded[i].status == FS_FALLBACK || fileneeded[i].status == FS_REQUESTED)) // FS_REQUESTED added
CONS_Printf(" %s\n", fileneeded[i].filename);
}
return false;
}
#endif
I_mkdir(downloaddir, 0755);
#ifdef PARANOIA
// Couldn't fit a single one in?
if (p == (char *)netbuffer->u.textcmd)
{
CONS_Printf("Direct download - fileneeded name for %s (fileneeded[%d]) too long??\n", (skippedafile != -1 ? fileneeded[skippedafile].filename : NULL), skippedafile);
return false;
}
#endif
WRITEUINT8(p, 0xFF); // terminator
if (!HSendPacket(servernode, true, 0, p - (char *)netbuffer->u.textcmd))
{
CONS_Printf("Direct download - unable to send packet.\n");
return false;
}
#ifdef MORELEGACYDOWNLOADER
if (skippedafile != -1)
{
firstloop = false;
goto tryagain;
}
#endif
#ifdef VERBOSEREQUESTFILE
CONS_Printf("Returning true\n");
#endif
return true;
}
// get request filepak and put it on the send queue
@ -411,16 +507,18 @@ boolean PT_RequestFile(INT32 node)
char wad[MAX_WADPATH+1];
UINT8 *p = netbuffer->u.textcmd;
UINT8 id;
while (p < netbuffer->u.textcmd + MAXTEXTCMD-1) // Don't allow hacked client to overflow
while (p < netbuffer->u.textcmd + MAXTEXTCMD) // Don't allow hacked client to overflow
{
id = READUINT8(p);
if (id == 0xFF)
break;
READSTRINGN(p, wad, MAX_WADPATH);
if (!AddFileToSendQueue(node, wad, id))
if (p >= netbuffer->u.textcmd + MAXTEXTCMD || !AddFileToSendQueue(node, wad, id))
{
if (cv_noticedownload.value)
CONS_Printf("Bad PT_REQUESTFILE from node %d!\n", node);
SV_AbortSendFiles(node);
return false; // don't read the rest of the files
return false; // don't read any more
}
}
return true; // no problems with any files
@ -552,7 +650,7 @@ boolean CL_LoadServerFiles(void)
continue; // Already loaded
else if (fileneeded[i].status == FS_FOUND)
{
P_AddWadFile(fileneeded[i].filename);
P_PartialAddWadFile(fileneeded[i].filename);
G_SetGameModified(true, false);
fileneeded[i].status = FS_OPEN;
return false;
@ -798,7 +896,7 @@ static boolean AddFileToSendQueue(INT32 node, const char *filename, UINT8 fileid
char wadfilename[MAX_WADPATH];
if (cv_noticedownload.value)
CONS_Printf("Sending file \"%s\" to node %d (%s)\n", filename, node, I_GetNodeAddress(node));
CONS_Printf("Sending file \"%s\" (id %d) to node %d (%s)\n", filename, fileid, node, I_GetNodeAddress(node));
// Find the last file in the list and set a pointer to its "next" field
q = &transfer[node].txlist;
@ -972,7 +1070,7 @@ static void SV_EndFileSend(INT32 node)
{
case SF_FILE: // It's a file, close it and free its filename
if (cv_noticedownload.value)
CONS_Printf("Ending file transfer for node %d\n", node);
CONS_Printf("Ending file transfer (id %d) for node %d\n", p->fileid, node);
if (transfer[node].currentfile)
fclose(transfer[node].currentfile);
free(p->id.filename);
@ -1750,6 +1848,7 @@ void CURLGetFile(void)
int msgs_left; /* how many messages are left */
const char *easy_handle_error;
long response_code = 0;
static char *filename;
if (curl_runninghandles)
{
@ -1774,6 +1873,8 @@ void CURLGetFile(void)
{
e = m->easy_handle;
easyres = m->data.result;
filename = Z_StrDup(curl_realname);
nameonly(filename);
if (easyres != CURLE_OK)
{
if (easyres == CURLE_HTTP_RETURNED_ERROR)
@ -1786,21 +1887,30 @@ void CURLGetFile(void)
curl_failedwebdownload = true;
fclose(curl_curfile->file);
remove(curl_curfile->filename);
curl_curfile->file = NULL;
//nameonly(curl_curfile->filename);
nameonly(curl_realname);
CONS_Printf(M_GetText("Failed to download %s (%s)\n"), curl_realname, easy_handle_error);
CONS_Printf(M_GetText("Failed to download %s (%s)\n"), filename, easy_handle_error);
}
else
{
nameonly(curl_realname);
CONS_Printf(M_GetText("Finished downloading %s\n"), curl_realname);
downloadcompletednum++;
downloadcompletedsize += curl_curfile->totalsize;
curl_curfile->status = FS_FOUND;
fclose(curl_curfile->file);
if (checkfilemd5(curl_curfile->filename, curl_curfile->md5sum) == FS_MD5SUMBAD)
{
CONS_Alert(CONS_ERROR, M_GetText("HTTP Download of %s finished but is corrupt or has been modified\n"), filename);
curl_curfile->status = FS_FALLBACK;
curl_failedwebdownload = true;
}
else
{
CONS_Printf(M_GetText("Finished HTTP download of %s\n"), filename);
downloadcompletednum++;
downloadcompletedsize += curl_curfile->totalsize;
curl_curfile->status = FS_FOUND;
}
}
Z_Free(filename);
curl_curfile->file = NULL;
curl_running = false;
curl_transfers--;
curl_multi_remove_handle(multi_handle, e);

View file

@ -168,7 +168,6 @@ typedef enum
KRITEM_DUALSNEAKER = NUMKARTITEMS,
KRITEM_TRIPLESNEAKER,
KRITEM_TRIPLEBANANA,
KRITEM_TENFOLDBANANA,
KRITEM_TRIPLEORBINAUT,
KRITEM_QUADORBINAUT,
KRITEM_DUALJAWZ,
@ -412,7 +411,7 @@ typedef struct player_s
// Basic gameplay things
UINT8 position; // Used for Kart positions, mostly for deterministic stuff
UINT8 oldposition; // Used for taunting when you pass someone
UINT8 positiondelay; // Used for position number, so it can grow when passing/being passed
UINT8 positiondelay; // Used for position number, so it can grow when passing
UINT32 distancetofinish;
waypoint_t *nextwaypoint;
respawnvars_t respawn; // Respawn info

View file

@ -3130,7 +3130,7 @@ void readcupheader(MYFILE *f, cupheader_t *cup)
void readfollower(MYFILE *f)
{
char *s;
char *word, *word2, dname[SKINNAMESIZE+1];
char *word, *word2;
char *tmp;
char testname[SKINNAMESIZE+1];
@ -3139,10 +3139,9 @@ void readfollower(MYFILE *f)
INT32 res;
INT32 i;
if (numfollowers > MAXSKINS)
if (numfollowers >= MAXSKINS)
{
deh_warning("Error: Too many followers, cannot add anymore.\n");
return;
I_Error("Out of Followers\nLoad less addons to fix this.");
}
s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL);
@ -3161,8 +3160,9 @@ void readfollower(MYFILE *f)
followers[numfollowers].bobspeed = TICRATE*2;
followers[numfollowers].bobamp = 4*FRACUNIT;
followers[numfollowers].hitconfirmtime = TICRATE;
followers[numfollowers].defaultcolor = SKINCOLOR_GREEN;
strcpy(followers[numfollowers].icon, "M_NORANK");
followers[numfollowers].defaultcolor = FOLLOWERCOLOR_MATCH;
followers[numfollowers].category = UINT8_MAX;
strcpy(followers[numfollowers].icon, "MISSING");
do
{
@ -3201,6 +3201,23 @@ void readfollower(MYFILE *f)
strcpy(followers[numfollowers].icon, word2);
nameset = true;
}
else if (fastcmp(word, "CATEGORY"))
{
INT32 j;
for (j = 0; j < numfollowercategories; j++)
{
if (!stricmp(followercategories[j].name, word2))
{
followers[numfollowers].category = j;
break;
}
}
if (j == numfollowercategories)
{
deh_warning("Follower %d: unknown follower category '%s'", numfollowers, word2);
}
}
else if (fastcmp(word, "MODE"))
{
if (word2)
@ -3215,7 +3232,20 @@ void readfollower(MYFILE *f)
}
else if (fastcmp(word, "DEFAULTCOLOR"))
{
followers[numfollowers].defaultcolor = get_number(word2);
INT32 j;
for (j = 0; j < numskincolors +2; j++) // +2 because of Match and Opposite
{
if (!stricmp(Followercolor_cons_t[j].strvalue, word2))
{
followers[numfollowers].defaultcolor = Followercolor_cons_t[j].value;
break;
}
}
if (j == numskincolors+2)
{
deh_warning("Follower %d: unknown follower color '%s'", numfollowers, word2);
}
}
else if (fastcmp(word, "SCALE"))
{
@ -3318,10 +3348,6 @@ void readfollower(MYFILE *f)
// set skin name (this is just the follower's name in lowercases):
// but before we do, let's... actually check if another follower isn't doing the same shit...
strcpy(testname, followers[numfollowers].name);
// lower testname for skin checks...
strlwr(testname);
res = K_FollowerAvailable(testname);
if (res > -1) // yikes, someone else has stolen our name already
{
@ -3333,8 +3359,7 @@ void readfollower(MYFILE *f)
// in that case, we'll be very lazy and copy numfollowers to the end of our skin name.
}
strcpy(followers[numfollowers].skinname, testname);
strcpy(dname, followers[numfollowers].skinname); // display name, just used for printing succesful stuff or errors later down the line.
strcpy(testname, followers[numfollowers].name);
// now that the skin name is ready, post process the actual name to turn the underscores into spaces!
for (i = 0; followers[numfollowers].name[i]; i++)
@ -3349,14 +3374,14 @@ void readfollower(MYFILE *f)
if (followers[numfollowers].mode < FOLLOWERMODE_FLOAT || followers[numfollowers].mode >= FOLLOWERMODE__MAX)
{
followers[numfollowers].mode = FOLLOWERMODE_FLOAT;
deh_warning("Follower '%s': Value for 'mode' should be between %d and %d.", dname, FOLLOWERMODE_FLOAT, FOLLOWERMODE__MAX-1);
deh_warning("Follower '%s': Value for 'mode' should be between %d and %d.", testname, FOLLOWERMODE_FLOAT, FOLLOWERMODE__MAX-1);
}
#define FALLBACK(field, field2, threshold, set) \
if ((signed)followers[numfollowers].field < threshold) \
{ \
followers[numfollowers].field = set; \
deh_warning("Follower '%s': Value for '%s' is too low! Minimum should be %d. Value was overwritten to %d.", dname, field2, threshold, set); \
deh_warning("Follower '%s': Value for '%s' is too low! Minimum should be %d. Value was overwritten to %d.", testname, field2, threshold, set); \
} \
FALLBACK(dist, "DIST", 0, 0);
@ -3373,13 +3398,6 @@ if ((signed)followers[numfollowers].field < threshold) \
#undef FALLBACK
// Special case for color I suppose
if (followers[numfollowers].defaultcolor > (unsigned)(numskincolors-1))
{
followers[numfollowers].defaultcolor = SKINCOLOR_GREEN;
deh_warning("Follower \'%s\': Value for 'color' should be between 1 and %d.\n", dname, numskincolors-1);
}
// also check if we forgot states. If we did, we will set any missing state to the follower's idlestate.
// Print a warning in case we don't have a fallback and set the state to S_INVISIBLE (rather than S_NULL) if unavailable.
@ -3388,7 +3406,7 @@ if (!followers[numfollowers].field) \
{ \
followers[numfollowers].field = fallbackstate ? fallbackstate : S_INVISIBLE; \
if (!fallbackstate) \
deh_warning("Follower '%s' is missing state definition for '%s', no idlestate fallback was found", dname, field2); \
deh_warning("Follower '%s' is missing state definition for '%s', no idlestate fallback was found", testname, field2); \
} \
NOSTATE(idlestate, "IDLESTATE");
@ -3399,11 +3417,83 @@ if (!followers[numfollowers].field) \
NOSTATE(hitconfirmstate, "HITCONFIRMSTATE");
#undef NOSTATE
CONS_Printf("Added follower '%s'\n", dname);
CONS_Printf("Added follower '%s'\n", testname);
if (followers[numfollowers].category < numfollowercategories)
followercategories[followers[numfollowers].category].numincategory++;
numfollowers++; // add 1 follower
Z_Free(s);
}
void readfollowercategory(MYFILE *f)
{
char *s;
char *word, *word2;
char *tmp;
boolean nameset;
if (numfollowercategories == MAXFOLLOWERCATEGORIES)
{
I_Error("Out of Follower categories\nLoad less addons to fix this.");
}
s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL);
// Ready the default variables for followers. We will overwrite them as we go! We won't set the name or states RIGHT HERE as this is handled down instead.
strcpy(followercategories[numfollowercategories].icon, "MISSING");
followercategories[numfollowercategories].numincategory = 0;
do
{
if (myfgets(s, MAXLINELEN, f))
{
if (s[0] == '\n')
break;
tmp = strchr(s, '#');
if (tmp)
*tmp = '\0';
if (s == tmp)
continue; // Skip comment lines, but don't break.
word = strtok(s, " ");
if (word)
strupr(word);
else
break;
word2 = strtok(NULL, " = ");
if (!word2)
break;
if (word2[strlen(word2)-1] == '\n')
word2[strlen(word2)-1] = '\0';
if (fastcmp(word, "NAME"))
{
strcpy(followercategories[numfollowercategories].name, word2);
nameset = true;
}
else if (fastcmp(word, "ICON"))
{
strcpy(followercategories[numfollowercategories].icon, word2);
nameset = true;
}
}
} while (!myfeof(f)); // finish when the line is empty
if (!nameset)
{
// well this is problematic.
strcpy(followercategories[numfollowercategories].name, va("Followercategory%d", numfollowercategories)); // this is lazy, so what
}
CONS_Printf("Added follower category '%s'\n", followercategories[numfollowercategories].name);
numfollowercategories++; // add 1 follower
Z_Free(s);
}
void readweather(MYFILE *f, INT32 num)
{
char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL);

View file

@ -82,6 +82,7 @@ void clear_conditionsets(void);
void readcupheader(MYFILE *f, cupheader_t *cup);
void readfollower(MYFILE *f);
void readfollowercategory(MYFILE *f);
preciptype_t get_precip(const char *word);
void readweather(MYFILE *f, INT32 num);

View file

@ -3488,6 +3488,11 @@ const char *const STATE_LIST[] = { // array length left dynamic for sanity testi
"S_BANANA",
"S_BANANA_DEAD",
"S_BANANA_SPARK",
"S_BANANA_SPARK2",
"S_BANANA_SPARK3",
"S_BANANA_SPARK4",
//{ Orbinaut
"S_ORBINAUT1",
"S_ORBINAUT2",
@ -5311,6 +5316,7 @@ const char *const MOBJTYPE_LIST[] = { // array length left dynamic for sanity t
"MT_BANANA", // Banana Stuff
"MT_BANANA_SHIELD",
"MT_BANANA_SPARK",
"MT_ORBINAUT", // Orbinaut stuff
"MT_ORBINAUT_SHIELD",
@ -5628,7 +5634,7 @@ const char *const MOBJFLAG_LIST[] = {
"SLIDEME",
"NOCLIP",
"FLOAT",
"BOXICON",
"SLOPE",
"MISSILE",
"SPRING",
"MONITOR",
@ -6756,14 +6762,13 @@ struct int_const_s const INT_CONST[] = {
// SRB2Kart
// kartitems_t
#define FOREACH( name, n ) { #name, KITEM_ ## name }
#define FOREACH( name, n ) { TOSTR (KITEM_ ## name), KITEM_ ## name }
KART_ITEM_ITERATOR, // Actual items (can be set for k_itemtype)
#undef FOREACH
{"NUMKARTITEMS",NUMKARTITEMS},
{"KRITEM_DUALSNEAKER",KRITEM_DUALSNEAKER}, // Additional roulette IDs (not usable for much in Lua besides K_GetItemPatch)
{"KRITEM_TRIPLESNEAKER",KRITEM_TRIPLESNEAKER},
{"KRITEM_TRIPLEBANANA",KRITEM_TRIPLEBANANA},
{"KRITEM_TENFOLDBANANA",KRITEM_TENFOLDBANANA},
{"KRITEM_TRIPLEORBINAUT",KRITEM_TRIPLEORBINAUT},
{"KRITEM_QUADORBINAUT",KRITEM_QUADORBINAUT},
{"KRITEM_DUALJAWZ",KRITEM_DUALJAWZ},

View file

@ -235,6 +235,12 @@ static void DEH_LoadDehackedFile(MYFILE *f, boolean mainfile)
readfollower(f);
continue;
}
else if (fastcmp(word, "FOLLOWERCATEGORY"))
{
// This is not a major mod.
readfollowercategory(f);
continue;
}
word2 = strtok(NULL, " ");
if (word2) {

View file

@ -398,6 +398,19 @@ typedef enum
SKINCOLOR_CHAOSEMERALD7,
SKINCOLOR_INVINCFLASH,
SKINCOLOR_POSNUM,
SKINCOLOR_POSNUM_WIN1,
SKINCOLOR_POSNUM_WIN2,
SKINCOLOR_POSNUM_WIN3,
SKINCOLOR_POSNUM_LOSE1,
SKINCOLOR_POSNUM_LOSE2,
SKINCOLOR_POSNUM_LOSE3,
SKINCOLOR_POSNUM_BEST1,
SKINCOLOR_POSNUM_BEST2,
SKINCOLOR_POSNUM_BEST3,
SKINCOLOR_POSNUM_BEST4,
SKINCOLOR_POSNUM_BEST5,
SKINCOLOR_POSNUM_BEST6,
SKINCOLOR_FIRSTFREESLOT,
SKINCOLOR_LASTFREESLOT = SKINCOLOR_FIRSTFREESLOT + NUMCOLORFREESLOTS - 1,

View file

@ -313,11 +313,11 @@ void G_ReadDemoExtraData(void)
demo_p += 16;
for (i = 0; i < numskincolors +2; i++) // +2 because of Match and Opposite
{
if (!stricmp(Followercolor_cons_t[i].strvalue, name))
{
players[p].followercolor = i;
break;
}
if (!stricmp(Followercolor_cons_t[i].strvalue, name))
{
players[p].followercolor = Followercolor_cons_t[i].value;
break;
}
}
}
if (extradata & DXD_PLAYSTATE)
@ -407,7 +407,7 @@ void G_ReadDemoExtraData(void)
void G_WriteDemoExtraData(void)
{
INT32 i;
INT32 i, j;
char name[16];
for (i = 0; i < MAXPLAYERS; i++)
@ -453,13 +453,18 @@ void G_WriteDemoExtraData(void)
if (players[i].followerskin == -1)
strncpy(name, "None", 16);
else
strncpy(name, followers[players[i].followerskin].skinname, 16);
strncpy(name, followers[players[i].followerskin].name, 16);
M_Memcpy(demo_p, name, 16);
demo_p += 16;
// write follower color
memset(name, 0, 16);
strncpy(name, Followercolor_cons_t[(UINT16)(players[i].followercolor+2)].strvalue, 16); // Not KartColor_Names because followercolor has extra values such as "Match"
for (j = (numskincolors+2)-1; j > 0; j--)
{
if (Followercolor_cons_t[j].value == players[i].followercolor)
break;
}
strncpy(name, Followercolor_cons_t[j].strvalue, 16); // Not KartColor_Names because followercolor has extra values such as "Match"
M_Memcpy(demo_p,name,16);
demo_p += 16;
@ -1951,7 +1956,7 @@ void G_RecordMetal(void)
void G_BeginRecording(void)
{
UINT8 i, p;
UINT8 i, j, p;
char name[MAXCOLORNAME+1];
player_t *player = &players[consoleplayer];
@ -2088,7 +2093,7 @@ void G_BeginRecording(void)
memset(name, 0, 16);
if (player->follower)
strncpy(name, followers[player->followerskin].skinname, 16);
strncpy(name, followers[player->followerskin].name, 16);
else
strncpy(name, "None", 16); // Say we don't have one, then.
@ -2097,7 +2102,12 @@ void G_BeginRecording(void)
// Save follower's colour
memset(name, 0, 16);
strncpy(name, Followercolor_cons_t[(UINT16)(player->followercolor+2)].strvalue, 16); // Not KartColor_Names because followercolor has extra values such as "Match"
for (j = (numskincolors+2)-1; j > 0; j--)
{
if (Followercolor_cons_t[j].value == players[i].followercolor)
break;
}
strncpy(name, Followercolor_cons_t[j].strvalue, 16); // Not KartColor_Names because followercolor has extra values such as "Match"
M_Memcpy(demo_p, name, 16);
demo_p += 16;
@ -2294,10 +2304,13 @@ static void G_LoadDemoExtraFiles(UINT8 **pp)
}
else
{
P_AddWadFile(filename);
P_PartialAddWadFile(filename);
}
}
}
if (P_PartialAddGetStage() >= 0)
P_MultiSetupWadFiles(true); // in case any partial adds were done
}
static void G_SkipDemoExtraFiles(UINT8 **pp)
@ -3070,11 +3083,11 @@ void G_DoPlayDemo(char *defdemoname)
demo_p += 16;
for (i = 0; i < numskincolors +2; i++) // +2 because of Match and Opposite
{
if (!stricmp(Followercolor_cons_t[i].strvalue, color))
{
players[p].followercolor = i;
break;
}
if (!stricmp(Followercolor_cons_t[i].strvalue, color))
{
players[p].followercolor = Followercolor_cons_t[i].value;
break;
}
}
// Score, since Kart uses this to determine where you start on the map

View file

@ -4263,8 +4263,26 @@ void G_EndGame(void)
// Sets a tad of default info we need.
void G_LoadGameSettings(void)
{
INT32 i;
// initialize free sfx slots for skin sounds
S_InitRuntimeSounds();
// Prepare skincolor material.
for (i = 0; i < MAXSKINCOLORS; i++)
{
Color_cons_t[i].value = Followercolor_cons_t[i+2].value = i;
Color_cons_t[i].strvalue = Followercolor_cons_t[i+2].strvalue = skincolors[i].name;
}
Followercolor_cons_t[1].value = FOLLOWERCOLOR_MATCH;
Followercolor_cons_t[1].strvalue = "Match"; // Add "Match" option, which will make the follower color match the player's
Followercolor_cons_t[0].value = FOLLOWERCOLOR_OPPOSITE;
Followercolor_cons_t[0].strvalue = "Opposite"; // Add "Opposite" option, ...which is like "Match", but for coloropposite.
Color_cons_t[MAXSKINCOLORS].value = Followercolor_cons_t[MAXSKINCOLORS+2].value = 0;
Color_cons_t[MAXSKINCOLORS].strvalue = Followercolor_cons_t[MAXSKINCOLORS+2].strvalue = NULL;
}
#define GD_VERSIONCHECK 0xBA5ED444 // Change every major version, as usual

View file

@ -326,22 +326,28 @@ patch_t *HU_UpdateOrBlankPatch(patch_t **user, boolean required, const char *for
va_list ap;
char buffer[9];
lumpnum_t lump;
lumpnum_t lump = INT16_MAX;
patch_t *patch;
va_start (ap, format);
vsnprintf(buffer, sizeof buffer, format, ap);
va_end (ap);
if (user && p_adding_file != INT16_MAX)
if (user && partadd_earliestfile != UINT16_MAX)
{
lump = W_CheckNumForNamePwad(buffer, p_adding_file, 0);
UINT16 fileref = numwadfiles;
lump = INT16_MAX;
while ((lump == INT16_MAX) && ((--fileref) >= partadd_earliestfile))
{
lump = W_CheckNumForNamePwad(buffer, fileref, 0);
}
/* no update in this wad */
if (lump == INT16_MAX)
if (fileref < partadd_earliestfile)
return *user;
lump |= (p_adding_file << 16);
lump |= (fileref << 16);
}
else
{

View file

@ -557,6 +557,7 @@ char sprnames[NUMSPRITES + 1][5] =
"RSHE", // Rocket sneaker
"FITM", // Eggman Monitor
"BANA", // Banana Peel
"BAND", // Banana Peel death particles
"ORBN", // Orbinaut
"JAWZ", // Jawz
"SSMN", // SS Mine
@ -4077,7 +4078,12 @@ state_t states[NUMSTATES] =
{SPR_FITM, 24|FF_FULLBRIGHT, 175, {NULL}, 0, 0, S_NULL}, // S_EGGMANITEM_DEAD
{SPR_BANA, 0, -1, {NULL}, 0, 0, S_NULL}, // S_BANANA
{SPR_BANA, 0, 175, {NULL}, 0, 0, S_NULL}, // S_BANANA_DEAD
{SPR_BANA, 1, 175, {NULL}, 0, 0, S_NULL}, // S_BANANA_DEAD
{SPR_BAND, 0, -1, {NULL}, 0, 0, S_BANANA_SPARK2}, // S_BANANA_SPARK
{SPR_BAND, 1|FF_FULLBRIGHT, 2, {NULL}, 0, 0, S_BANANA_SPARK3}, // S_BANANA_SPARK2
{SPR_BAND, 2|FF_FULLBRIGHT, 2, {NULL}, 0, 0, S_BANANA_SPARK4}, // S_BANANA_SPARK3
{SPR_BAND, 3|FF_FULLBRIGHT, -1, {NULL}, 0, 0, S_NULL}, // S_BANANA_SPARK4
{SPR_ORBN, 0, 1, {NULL}, 0, 0, S_ORBINAUT2}, // S_ORBINAUT1
{SPR_ORBN, 1, 1, {NULL}, 0, 0, S_ORBINAUT3}, // S_ORBINAUT2
@ -5289,7 +5295,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
1000, // mass
MT_THOK, // damage
sfx_None, // activesound
MF_SOLID|MF_SHOOTABLE|MF_DONTENCOREMAP|MF_APPLYTERRAIN, // flags
MF_SOLID|MF_SHOOTABLE|MF_DONTENCOREMAP|MF_APPLYTERRAIN|MF_SLOPE, // flags
(statenum_t)MT_THOK // raisestate
},
@ -5316,7 +5322,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
1000, // mass
0, // damage
sfx_None, // activesound
MF_SOLID|MF_DONTENCOREMAP|MF_APPLYTERRAIN, // flags
MF_SOLID|MF_DONTENCOREMAP|MF_APPLYTERRAIN|MF_SLOPE, // flags
S_NULL // raisestate
},
@ -9986,7 +9992,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
100, // mass
62*FRACUNIT, // damage
sfx_None, // activesound
MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY|MF_NOGRAVITY|MF_BOXICON, // flags
MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY|MF_NOGRAVITY, // flags
S_NULL // raisestate
},
@ -10013,7 +10019,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
100, // mass
62*FRACUNIT, // damage
sfx_None, // activesound
MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY|MF_NOGRAVITY|MF_BOXICON, // flags
MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY|MF_NOGRAVITY, // flags
S_NULL // raisestate
},
@ -10040,7 +10046,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
100, // mass
62*FRACUNIT, // damage
sfx_None, // activesound
MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY|MF_NOGRAVITY|MF_BOXICON, // flags
MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY|MF_NOGRAVITY, // flags
S_NULL // raisestate
},
@ -10067,7 +10073,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
100, // mass
62*FRACUNIT, // damage
sfx_None, // activesound
MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY|MF_NOGRAVITY|MF_BOXICON, // flags
MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY|MF_NOGRAVITY, // flags
S_NULL // raisestate
},
@ -10094,7 +10100,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
100, // mass
62*FRACUNIT, // damage
sfx_None, // activesound
MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY|MF_NOGRAVITY|MF_BOXICON, // flags
MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY|MF_NOGRAVITY, // flags
S_NULL // raisestate
},
@ -10121,7 +10127,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
100, // mass
62*FRACUNIT, // damage
sfx_None, // activesound
MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY|MF_NOGRAVITY|MF_BOXICON, // flags
MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY|MF_NOGRAVITY, // flags
S_NULL // raisestate
},
@ -10148,7 +10154,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
100, // mass
62*FRACUNIT, // damage
sfx_None, // activesound
MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY|MF_NOGRAVITY|MF_BOXICON, // flags
MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY|MF_NOGRAVITY, // flags
S_NULL // raisestate
},
@ -10175,7 +10181,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
100, // mass
62*FRACUNIT, // damage
sfx_None, // activesound
MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY|MF_NOGRAVITY|MF_BOXICON, // flags
MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY|MF_NOGRAVITY, // flags
S_NULL // raisestate
},
@ -10202,7 +10208,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
100, // mass
62*FRACUNIT, // damage
sfx_None, // activesound
MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY|MF_NOGRAVITY|MF_BOXICON, // flags
MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY|MF_NOGRAVITY, // flags
S_NULL // raisestate
},
@ -10229,7 +10235,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
100, // mass
62*FRACUNIT, // damage
sfx_None, // activesound
MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY|MF_NOGRAVITY|MF_BOXICON, // flags
MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY|MF_NOGRAVITY, // flags
S_NULL // raisestate
},
@ -10256,7 +10262,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
100, // mass
62*FRACUNIT, // damage
sfx_None, // activesound
MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY|MF_NOGRAVITY|MF_BOXICON, // flags
MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY|MF_NOGRAVITY, // flags
S_NULL // raisestate
},
@ -10283,7 +10289,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
100, // mass
62*FRACUNIT, // damage
sfx_None, // activesound
MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY|MF_NOGRAVITY|MF_BOXICON, // flags
MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY|MF_NOGRAVITY, // flags
S_NULL // raisestate
},
@ -10310,7 +10316,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
100, // mass
62*FRACUNIT, // damage
sfx_None, // activesound
MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY|MF_NOGRAVITY|MF_BOXICON, // flags
MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY|MF_NOGRAVITY, // flags
S_NULL // raisestate
},
@ -10337,7 +10343,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
100, // mass
62*FRACUNIT, // damage
sfx_None, // activesound
MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY|MF_NOGRAVITY|MF_BOXICON, // flags
MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY|MF_NOGRAVITY, // flags
S_NULL // raisestate
},
@ -10364,7 +10370,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
100, // mass
62*FRACUNIT, // damage
sfx_None, // activesound
MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY|MF_NOGRAVITY|MF_BOXICON, // flags
MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY|MF_NOGRAVITY, // flags
S_NULL // raisestate
},
@ -10391,7 +10397,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
100, // mass
62*FRACUNIT, // damage
sfx_None, // activesound
MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY|MF_NOGRAVITY|MF_BOXICON, // flags
MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY|MF_NOGRAVITY, // flags
S_NULL // raisestate
},
@ -10418,7 +10424,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
100, // mass
62*FRACUNIT, // damage
sfx_None, // activesound
MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY|MF_NOGRAVITY|MF_BOXICON, // flags
MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY|MF_NOGRAVITY, // flags
S_NULL // raisestate
},
@ -10445,7 +10451,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
100, // mass
62*FRACUNIT, // damage
sfx_None, // activesound
MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY|MF_NOGRAVITY|MF_BOXICON, // flags
MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY|MF_NOGRAVITY, // flags
S_NULL // raisestate
},
@ -10472,7 +10478,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
100, // mass
62*FRACUNIT, // damage
sfx_None, // activesound
MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY|MF_NOGRAVITY|MF_BOXICON, // flags
MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY|MF_NOGRAVITY, // flags
S_NULL // raisestate
},
@ -23090,7 +23096,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
100, // mass
1, // damage
sfx_peel, // activesound
MF_SHOOTABLE|MF_DONTENCOREMAP|MF_APPLYTERRAIN, // flags
MF_SHOOTABLE|MF_DONTENCOREMAP|MF_APPLYTERRAIN|MF_SLOPE, // flags
S_NULL // raisestate
},
@ -23117,7 +23123,34 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
100, // mass
1, // damage
sfx_None, // activesound
MF_SHOOTABLE|MF_NOGRAVITY|MF_SCENERY|MF_DONTENCOREMAP|MF_APPLYTERRAIN, // flags
MF_SHOOTABLE|MF_NOGRAVITY|MF_SCENERY|MF_DONTENCOREMAP|MF_APPLYTERRAIN|MF_SLOPE, // flags
S_NULL // raisestate
},
{ // MT_BANANA_SPARK
-1, // doomednum
S_BANANA_SPARK, // spawnstate
1000, // spawnhealth
S_NULL, // seestate
sfx_None, // seesound
8, // reactiontime
sfx_None, // attacksound
S_NULL, // painstate
0, // painchance
sfx_None, // painsound
S_NULL, // meleestate
S_NULL, // missilestate
S_BANANA_SPARK2,// deathstate
S_NULL, // xdeathstate
sfx_None, // deathsound
0, // speed
4*FRACUNIT, // radius
8*FRACUNIT, // height
0, // display offset
100, // mass
1, // damage
sfx_None, // activesound
MF_DONTENCOREMAP|MF_NOCLIPTHING|MF_NOSQUISH, // flags
S_NULL // raisestate
},
@ -23144,7 +23177,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
100, // mass
1, // damage
sfx_s3k96, // activesound
MF_SHOOTABLE|MF_DONTENCOREMAP|MF_APPLYTERRAIN, // flags
MF_SHOOTABLE|MF_DONTENCOREMAP|MF_APPLYTERRAIN|MF_SLOPE, // flags
S_NULL // raisestate
},
@ -23171,7 +23204,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
100, // mass
1, // damage
sfx_None, // activesound
MF_SHOOTABLE|MF_NOGRAVITY|MF_SCENERY|MF_DONTENCOREMAP|MF_APPLYTERRAIN, // flags
MF_SHOOTABLE|MF_NOGRAVITY|MF_SCENERY|MF_DONTENCOREMAP|MF_APPLYTERRAIN|MF_SLOPE, // flags
S_NULL // raisestate
},
@ -23198,7 +23231,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
100, // mass
1, // damage
sfx_s3kc0s, // activesound
MF_SHOOTABLE|MF_DONTENCOREMAP|MF_APPLYTERRAIN, // flags
MF_SHOOTABLE|MF_DONTENCOREMAP|MF_APPLYTERRAIN|MF_SLOPE, // flags
S_NULL // raisestate
},
@ -23225,7 +23258,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
100, // mass
1, // damage
sfx_None, // activesound
MF_SHOOTABLE|MF_NOGRAVITY|MF_SCENERY|MF_DONTENCOREMAP|MF_APPLYTERRAIN, // flags
MF_SHOOTABLE|MF_NOGRAVITY|MF_SCENERY|MF_DONTENCOREMAP|MF_APPLYTERRAIN|MF_SLOPE, // flags
S_NULL // raisestate
},
@ -23279,7 +23312,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
100, // mass
1, // damage
sfx_s3k5c, // activesound
MF_SHOOTABLE|MF_DONTENCOREMAP|MF_APPLYTERRAIN, // flags
MF_SHOOTABLE|MF_DONTENCOREMAP|MF_APPLYTERRAIN|MF_SLOPE, // flags
S_NULL // raisestate
},
@ -23306,7 +23339,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
100, // mass
1, // damage
sfx_None, // activesound
MF_SHOOTABLE|MF_NOGRAVITY|MF_SCENERY|MF_DONTENCOREMAP|MF_APPLYTERRAIN, // flags
MF_SHOOTABLE|MF_NOGRAVITY|MF_SCENERY|MF_DONTENCOREMAP|MF_APPLYTERRAIN|MF_SLOPE, // flags
S_NULL // raisestate
},
@ -23414,7 +23447,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
0, // mass
0, // damage
sfx_s3k5c, // activesound
MF_SHOOTABLE|MF_DONTENCOREMAP|MF_APPLYTERRAIN, // flags
MF_SHOOTABLE|MF_DONTENCOREMAP|MF_APPLYTERRAIN|MF_SLOPE, // flags
S_NULL // raisestate
},
@ -23441,7 +23474,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
100, // mass
1, // damage
sfx_s3k96, // activesound
MF_SPECIAL|MF_DONTENCOREMAP|MF_APPLYTERRAIN, // flags
MF_SPECIAL|MF_DONTENCOREMAP|MF_APPLYTERRAIN|MF_SLOPE, // flags
S_NULL // raisestate
},
@ -23468,7 +23501,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
100, // mass
1, // damage
sfx_None, // activesound
MF_SPECIAL|MF_NOGRAVITY|MF_SCENERY|MF_DONTENCOREMAP|MF_APPLYTERRAIN, // flags
MF_SPECIAL|MF_NOGRAVITY|MF_SCENERY|MF_DONTENCOREMAP|MF_APPLYTERRAIN|MF_SLOPE, // flags
S_NULL // raisestate
},
@ -24089,7 +24122,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
100, // mass
1, // damage
sfx_None, // activesound
MF_NOGRAVITY|MF_SCENERY|MF_DONTENCOREMAP|MF_APPLYTERRAIN, // flags
MF_NOGRAVITY|MF_SCENERY|MF_DONTENCOREMAP|MF_APPLYTERRAIN|MF_SLOPE, // flags
S_NULL // raisestate
},
@ -29126,7 +29159,21 @@ skincolor_t skincolors[MAXSKINCOLORS] = {
{"Chaos Emerald 6", { 0, 208, 50, 32, 34, 37, 40, 44, 0, 0, 0, 0, 0, 0, 0, 0}, SKINCOLOR_NONE, 0, 0, false}, // SKINCOLOR_CHAOSEMERALD6
{"Chaos Emerald 7", { 0, 120, 121, 140, 133, 135, 149, 156, 0, 0, 0, 0, 0, 0, 0, 0}, SKINCOLOR_NONE, 0, 0, false}, // SKINCOLOR_CHAOSEMERALD7
{"Invinc Flash", { 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}, SKINCOLOR_NONE, 0, 0, false} // SKINCOLOR_INVINCFLASH
{"Invinc Flash", { 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}, SKINCOLOR_NONE, 0, 0, false}, // SKINCOLOR_INVINCFLASH
{"Position", { 8, 9, 11, 12, 14, 15, 17, 18, 20, 21, 23, 24, 26, 27, 29, 30}, SKINCOLOR_NONE, 0, 0, false}, // SKINCOLOR_POSNUM
{"Position Win 1", {152, 152, 153, 153, 154, 154, 155, 155, 156, 156, 157, 158, 159, 253, 254, 30}, SKINCOLOR_NONE, 0, 0, false}, // SKINCOLOR_POSNUM_WIN1
{"Position Win 2", {134, 134, 135, 135, 135, 136, 136, 136, 137, 137, 138, 138, 139, 139, 254, 30}, SKINCOLOR_NONE, 0, 0, false}, // SKINCOLOR_POSNUM_WIN2
{"Position Win 3", {255, 255, 122, 122, 123, 123, 141, 141, 142, 142, 143, 143, 138, 139, 254, 30}, SKINCOLOR_NONE, 0, 0, false}, // SKINCOLOR_POSNUM_WIN3
{"Position Lose 1", { 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 71, 46, 47, 29, 30}, SKINCOLOR_NONE, 0, 0, false}, // SKINCOLOR_POSNUM_LOSE1
{"Position Lose 2", { 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 63, 44, 45, 46, 47, 30}, SKINCOLOR_NONE, 0, 0, false}, // SKINCOLOR_POSNUM_LOSE2
{"Position Lose 3", { 73, 74, 75, 76, 76, 77, 77, 78, 78, 79, 79, 236, 237, 238, 239, 30}, SKINCOLOR_NONE, 0, 0, false}, // SKINCOLOR_POSNUM_LOSE3
{"Position Best 1", { 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 71, 46, 47, 29, 30}, SKINCOLOR_NONE, 0, 0, false}, // SKINCOLOR_POSNUM_BEST1
{"Position Best 2", { 73, 74, 75, 76, 76, 77, 77, 78, 78, 79, 79, 236, 237, 238, 239, 30}, SKINCOLOR_NONE, 0, 0, false}, // SKINCOLOR_POSNUM_BEST2
{"Position Best 3", {112, 112, 113, 114, 115, 115, 116, 116, 117, 117, 118, 118, 119, 110, 111, 30}, SKINCOLOR_NONE, 0, 0, false}, // SKINCOLOR_POSNUM_BEST3
{"Position Best 4", {255, 255, 122, 122, 123, 123, 141, 141, 142, 142, 143, 143, 138, 139, 254, 30}, SKINCOLOR_NONE, 0, 0, false}, // SKINCOLOR_POSNUM_BEST4
{"Position Best 5", {152, 152, 153, 153, 154, 154, 155, 155, 156, 156, 157, 158, 159, 253, 254, 30}, SKINCOLOR_NONE, 0, 0, false}, // SKINCOLOR_POSNUM_BEST5
{"Position Best 6", {181, 181, 182, 182, 183, 183, 184, 184, 185, 185, 186, 186, 187, 187, 29, 30}, SKINCOLOR_NONE, 0, 0, false} // SKINCOLOR_POSNUM_BEST6
};
/** Patches the mobjinfo, state, and skincolor tables.

View file

@ -1103,6 +1103,7 @@ typedef enum sprite
SPR_RSHE, // Rocket sneaker
SPR_FITM, // Eggman Monitor
SPR_BANA, // Banana Peel
SPR_BAND, // Banana Peel death particles
SPR_ORBN, // Orbinaut
SPR_JAWZ, // Jawz
SPR_SSMN, // SS Mine
@ -4500,6 +4501,11 @@ typedef enum state
S_BANANA,
S_BANANA_DEAD,
S_BANANA_SPARK,
S_BANANA_SPARK2,
S_BANANA_SPARK3,
S_BANANA_SPARK4,
//{ Orbinaut
S_ORBINAUT1,
S_ORBINAUT2,
@ -6359,6 +6365,7 @@ typedef enum mobj_type
MT_BANANA, // Banana Stuff
MT_BANANA_SHIELD,
MT_BANANA_SPARK,
MT_ORBINAUT, // Orbinaut stuff
MT_ORBINAUT_SHIELD,

View file

@ -94,7 +94,7 @@ boolean K_BananaBallhogCollide(mobj_t *t1, mobj_t *t2)
S_StartSound(t2, t2->info->deathsound);
P_KillMobj(t2, t1, t1, DMG_NORMAL);
P_SetObjectMomZ(t2, 8*FRACUNIT, false);
P_SetObjectMomZ(t2, 24*FRACUNIT, false);
P_InstaThrust(t2, bounceangle, 16*FRACUNIT);
P_SpawnMobj(t2->x/2 + t1->x/2, t2->y/2 + t1->y/2, t2->z/2 + t1->z/2, MT_ITEMCLASH);
@ -122,7 +122,7 @@ boolean K_BananaBallhogCollide(mobj_t *t1, mobj_t *t2)
S_StartSound(t1, t1->info->deathsound);
P_KillMobj(t1, t2, t2, DMG_NORMAL);
P_SetObjectMomZ(t1, 8*FRACUNIT, false);
P_SetObjectMomZ(t1, 24*FRACUNIT, false);
P_InstaThrust(t1, bounceangle, 16*FRACUNIT);
}
@ -351,7 +351,7 @@ boolean K_MineCollide(mobj_t *t1, mobj_t *t2)
S_StartSound(t2, t2->info->deathsound);
P_KillMobj(t2, t1, t1, DMG_NORMAL);
P_SetObjectMomZ(t2, 8*FRACUNIT, false);
P_SetObjectMomZ(t2, 24*FRACUNIT, false);
P_InstaThrust(t2, bounceangle, 16*FRACUNIT);
}
else if (t2->flags & MF_SHOOTABLE)
@ -414,7 +414,7 @@ boolean K_LandMineCollide(mobj_t *t1, mobj_t *t2)
S_StartSound(t2, t2->info->deathsound);
P_KillMobj(t2, t1, t1, DMG_NORMAL);
P_SetObjectMomZ(t2, 8*FRACUNIT, false);
P_SetObjectMomZ(t2, 24*FRACUNIT, false);
P_InstaThrust(t2, bounceangle, 16*FRACUNIT);
P_SpawnMobj(t2->x/2 + t1->x/2, t2->y/2 + t1->y/2, t2->z/2 + t1->z/2, MT_ITEMCLASH);

View file

@ -15,6 +15,9 @@
INT32 numfollowers = 0;
follower_t followers[MAXSKINS];
INT32 numfollowercategories;
followercategory_t followercategories[MAXFOLLOWERCATEGORIES];
CV_PossibleValue_t Followercolor_cons_t[MAXSKINCOLORS+3]; // +3 to account for "Match", "Opposite" & NULL
/*--------------------------------------------------
@ -28,7 +31,7 @@ INT32 K_FollowerAvailable(const char *name)
for (i = 0; i < numfollowers; i++)
{
if (stricmp(followers[i].skinname, name) == 0)
if (stricmp(followers[i].name, name) == 0)
return i;
}
@ -54,7 +57,7 @@ boolean K_SetFollowerByName(INT32 playernum, const char *skinname)
for (i = 0; i < numfollowers; i++)
{
// search in the skin list
if (stricmp(followers[i].skinname, skinname) == 0)
if (stricmp(followers[i].name, skinname) == 0)
{
K_SetFollowerByNum(playernum, i);
return true;
@ -74,6 +77,31 @@ boolean K_SetFollowerByName(INT32 playernum, const char *skinname)
return false;
}
/*--------------------------------------------------
void K_RemoveFollower(player_t *player)
See header file for description.
--------------------------------------------------*/
void K_RemoveFollower(player_t *player)
{
mobj_t *bub, *tmp;
if (player->follower && !P_MobjWasRemoved(player->follower)) // this is also called when we change colour so don't respawn the follower unless we changed skins
{
// Remove follower's possible hnext list (bubble)
bub = player->follower->hnext;
while (bub && !P_MobjWasRemoved(bub))
{
tmp = bub->hnext;
P_RemoveMobj(bub);
bub = tmp;
}
P_RemoveMobj(player->follower);
P_SetTarget(&player->follower, NULL);
}
}
/*--------------------------------------------------
void K_SetFollowerByNum(INT32 playernum, INT32 skinnum)
@ -82,8 +110,6 @@ boolean K_SetFollowerByName(INT32 playernum, const char *skinname)
void K_SetFollowerByNum(INT32 playernum, INT32 skinnum)
{
player_t *player = &players[playernum];
mobj_t *bub;
mobj_t *tmp;
player->followerready = true; // we are ready to perform follower related actions in the player thinker, now.
@ -94,21 +120,8 @@ void K_SetFollowerByNum(INT32 playernum, INT32 skinnum)
However, we will despawn it right here if there's any to make it easy for the player thinker to replace it or delete it.
*/
if (player->follower && skinnum != player->followerskin) // this is also called when we change colour so don't respawn the follower unless we changed skins
{
// Remove follower's possible hnext list (bubble)
bub = player->follower->hnext;
while (bub && !P_MobjWasRemoved(bub))
{
tmp = bub->hnext;
P_RemoveMobj(bub);
bub = tmp;
}
P_RemoveMobj(player->follower);
P_SetTarget(&player->follower, NULL);
}
if (skinnum != player->followerskin)
K_RemoveFollower(player);
player->followerskin = skinnum;
@ -253,18 +266,16 @@ void K_HandleFollower(player_t *player)
{
//CONS_Printf("Follower skin invlaid. Setting to -1.\n");
player->followerskin = -1;
return;
}
// don't do anything if we can't have a follower to begin with.
// (It gets removed under those conditions)
if (player->spectator)
{
return;
}
if (player->followerskin < 0)
if (player->spectator || player->followerskin < 0)
{
if (player->follower)
{
K_RemoveFollower(player);
}
return;
}

View file

@ -45,10 +45,11 @@ typedef enum
//
typedef struct follower_s
{
char skinname[SKINNAMESIZE+1]; // Skin Name. This is what to refer to when asking the commands anything.
char name[SKINNAMESIZE+1]; // Name. This is used for the menus. We'll just follow the same rules as skins for this.
char name[SKINNAMESIZE+1]; // Skin Name. This is what to refer to when asking the commands anything..
char icon[8+1]; // Lump names are only 8 characters. (+1 for \0)
UINT8 category; // Category
skincolornum_t defaultcolor; // default color for menus.
followermode_t mode; // Follower behavior modifier.
@ -85,6 +86,18 @@ typedef struct follower_s
extern INT32 numfollowers;
extern follower_t followers[MAXSKINS];
#define MAXFOLLOWERCATEGORIES 32
typedef struct followercategory_s
{
char name[SKINNAMESIZE+1]; // Name. This is used for the menus. We'll just follow the same rules as skins for this.
char icon[8+1]; // Lump names are only 8 characters. (+1 for \0)
UINT8 numincategory;
} followercategory_t;
extern INT32 numfollowercategories;
extern followercategory_t followercategories[MAXFOLLOWERCATEGORIES];
/*--------------------------------------------------
INT32 K_FollowerAvailable(const char *name)
@ -168,5 +181,19 @@ UINT16 K_GetEffectiveFollowerColor(UINT16 followercolor, UINT16 playercolor);
void K_HandleFollower(player_t *player);
/*--------------------------------------------------
void K_RemoveFollower(player_t *player)
Removes Follower object
Input Arguments:-
player - The player who we want to remove the follower of.
Return:-
None
--------------------------------------------------*/
void K_RemoveFollower(player_t *player);
#endif // __K_FOLLOWER__

View file

@ -676,7 +676,7 @@ void K_PlayerLoseLife(player_t *player)
return;
}
if (player->spectator || player->exiting || player->bot || (player->pflags & PF_LOSTLIFE))
if (player->spectator || player->exiting || player->bot || player->lives <= 0 || (player->pflags & PF_LOSTLIFE))
{
return;
}

View file

@ -37,10 +37,6 @@
#include "r_fps.h"
#include "m_random.h"
#define NUMPOSNUMS 10
#define NUMPOSFRAMES 7 // White, three blues, three reds
#define NUMWINFRAMES 6 // Red, yellow, green, cyan, blue, purple
//{ Patch Definitions
static patch_t *kp_nodraw;
@ -70,8 +66,7 @@ static patch_t *kp_startcountdown[20];
static patch_t *kp_racefault[6];
static patch_t *kp_racefinish[6];
static patch_t *kp_positionnum[NUMPOSNUMS][NUMPOSFRAMES];
static patch_t *kp_winnernum[NUMPOSFRAMES];
static patch_t *kp_positionnum[10][2][2]; // number, overlay or underlay, splitscreen
static patch_t *kp_facenum[MAXPLAYERS+1];
static patch_t *kp_facehighlight[8];
@ -179,7 +174,7 @@ static patch_t *kp_trickcool[2];
void K_LoadKartHUDGraphics(void)
{
INT32 i, j;
INT32 i, j, k;
char buffer[9];
// Null Stuff
@ -280,23 +275,29 @@ void K_LoadKartHUDGraphics(void)
HU_UpdatePatch(&kp_racefinish[5], "K_2PFINB");
// Position numbers
sprintf(buffer, "K_POSNxx");
for (i = 0; i < NUMPOSNUMS; i++)
sprintf(buffer, "KRNKxyz");
for (i = 0; i < 10; i++)
{
buffer[6] = '0'+i;
for (j = 0; j < NUMPOSFRAMES; j++)
{
//sprintf(buffer, "K_POSN%d%d", i, j);
buffer[7] = '0'+j;
HU_UpdatePatch(&kp_positionnum[i][j], "%s", buffer);
}
}
sprintf(buffer, "K_POSNWx");
for (i = 0; i < NUMWINFRAMES; i++)
{
buffer[7] = '0'+i;
HU_UpdatePatch(&kp_winnernum[i], "%s", buffer);
for (j = 0; j < 2; j++)
{
buffer[5] = 'A'+j;
for (k = 0; k < 2; k++)
{
if (k > 0)
{
buffer[4] = 'S';
}
else
{
buffer[4] = 'B';
}
HU_UpdatePatch(&kp_positionnum[i][j][k], "%s", buffer);
}
}
}
sprintf(buffer, "OPPRNKxx");
@ -659,7 +660,6 @@ const char *K_GetItemPatch(UINT8 item, boolean tiny)
return (tiny ? "K_ISINV1" : "K_ITINV1");
case KITEM_BANANA:
case KRITEM_TRIPLEBANANA:
case KRITEM_TENFOLDBANANA:
return (tiny ? "K_ISBANA" : "K_ITBANA");
case KITEM_EGGMAN:
return (tiny ? "K_ISEGGM" : "K_ITEGGM");
@ -950,6 +950,11 @@ void K_ObjectTracking(trackingResult_t *result, vector3_t *point, boolean revers
h = R_PointToDist2(point->x, point->y, viewx, viewy);
da = AngleDeltaSigned(viewpointAngle, R_PointToAngle2(point->x, point->y, viewx, viewy));
if (reverse)
{
da = -(da);
}
// Set results relative to top left!
result->x = FixedMul(NEWTAN(da), fg);
result->y = FixedMul((NEWTAN(viewpointAiming) - FixedDiv((viewz - point->z), 1 + FixedMul(NEWCOS(da), h))), fg);
@ -1545,146 +1550,187 @@ bademblem:
}
}
static void K_DrawKartPositionNum(INT32 num)
static fixed_t K_DrawKartPositionNumPatch(UINT8 num, UINT8 *color, fixed_t x, fixed_t y, fixed_t scale, INT32 flags)
{
// POSI_X = BASEVIDWIDTH - 51; // 269
// POSI_Y = BASEVIDHEIGHT- 64; // 136
UINT8 splitIndex = (r_splitscreen > 0) ? 1 : 0;
fixed_t w = FRACUNIT;
fixed_t h = FRACUNIT;
INT32 overlayFlags[2];
INT32 i;
boolean win = (stplyr->exiting && num == 1);
//INT32 X = POSI_X;
INT32 W = SHORT(kp_positionnum[0][0]->width);
fixed_t scale = FRACUNIT;
patch_t *localpatch = kp_positionnum[0][0];
INT32 fx = 0, fy = 0, fflags = 0;
INT32 addOrSub = V_ADD;
boolean flipdraw = false; // flip the order we draw it in for MORE splitscreen bs. fun.
boolean flipvdraw = false; // used only for 2p splitscreen so overtaking doesn't make 1P's position fly off the screen.
boolean overtake = false;
if (num >= 10)
{
return x; // invalid input
}
if ((mapheaderinfo[gamemap - 1]->levelflags & LF_SUBTRACTNUM) == LF_SUBTRACTNUM)
{
addOrSub = V_SUBTRACT;
overlayFlags[0] = V_SUBTRACT;
overlayFlags[1] = V_ADD;
}
else
{
overlayFlags[0] = V_ADD;
overlayFlags[1] = V_SUBTRACT;
}
w = kp_positionnum[num][0][splitIndex]->width * scale;
h = kp_positionnum[num][0][splitIndex]->height * scale;
if (flags & V_SNAPTORIGHT)
{
x -= w;
}
if (flags & V_SNAPTOBOTTOM)
{
y -= h;
}
for (i = 1; i >= 0; i--)
{
V_DrawFixedPatch(
x, y, scale,
flags | overlayFlags[i],
kp_positionnum[num][i][splitIndex],
color
);
}
return (x - w);
}
static void K_DrawKartPositionNum(INT32 num)
{
const tic_t counter = (leveltime / 3); // Alternate colors every three frames
fixed_t scale = FRACUNIT;
fixed_t fx = 0, fy = 0;
transnum_t trans = 0;
INT32 fflags = 0;
UINT8 *color = NULL;
if (leveltime < (starttime + NUMTRANSMAPS))
{
trans = (starttime + NUMTRANSMAPS) - leveltime;
}
if (trans >= NUMTRANSMAPS)
{
return;
}
if (stplyr->positiondelay || stplyr->exiting)
{
scale *= 2;
overtake = true; // this is used for splitscreen stuff in conjunction with flipdraw.
const UINT8 delay = (stplyr->exiting) ? POS_DELAY_TIME : stplyr->positiondelay;
const fixed_t add = (scale * 3) >> ((r_splitscreen == 1) ? 1 : 2);
scale += min((add * (delay * delay)) / (POS_DELAY_TIME * POS_DELAY_TIME), add);
}
if (r_splitscreen)
{
scale /= 2;
}
W = FixedMul(W<<FRACBITS, scale)>>FRACBITS;
// pain and suffering defined below
if (!r_splitscreen)
{
fx = POSI_X;
fy = BASEVIDHEIGHT - 8;
fflags = V_SNAPTOBOTTOM|V_SNAPTORIGHT|V_SPLITSCREEN;
fx = BASEVIDWIDTH << FRACBITS;
fy = BASEVIDHEIGHT << FRACBITS;
fflags = V_SNAPTOBOTTOM|V_SNAPTORIGHT;
}
else if (r_splitscreen == 1) // for this splitscreen, we'll use case by case because it's a bit different.
{
fx = POSI_X;
if (stplyr == &players[displayplayers[0]]) // for player 1: display this at the top right, above the minimap.
fx = BASEVIDWIDTH << FRACBITS;
if (stplyr == &players[displayplayers[0]])
{
fy = 30;
fflags = V_SNAPTOTOP|V_SNAPTORIGHT|V_SPLITSCREEN;
if (overtake)
flipvdraw = true; // make sure overtaking doesn't explode us
// for player 1: display this at the top right, above the minimap.
fy = 0;
fflags = V_SNAPTOTOP|V_SNAPTORIGHT;
}
else // if we're not p1, that means we're p2. display this at the bottom right, below the minimap.
else
{
fy = (BASEVIDHEIGHT/2) - 8;
fflags = V_SNAPTOBOTTOM|V_SNAPTORIGHT|V_SPLITSCREEN;
// if we're not p1, that means we're p2. display this at the bottom right, below the minimap.
fy = BASEVIDHEIGHT << FRACBITS;
fflags = V_SNAPTOBOTTOM|V_SNAPTORIGHT;
}
fy >>= 1;
}
else
{
fy = BASEVIDHEIGHT << FRACBITS;
if (stplyr == &players[displayplayers[0]]
|| stplyr == &players[displayplayers[2]])
{
// If we are P1 or P3...
fx = 0;
fflags = V_SNAPTOLEFT|V_SNAPTOBOTTOM;
}
else
{
// else, that means we're P2 or P4.
fx = BASEVIDWIDTH << FRACBITS;
fflags = V_SNAPTORIGHT|V_SNAPTOBOTTOM;
}
fx >>= 1;
fy >>= 1;
}
if (trans > 0)
{
fflags |= (trans << V_ALPHASHIFT);
}
if (stplyr->exiting && num == 1)
{
// 1st place winner? You get rainbows!!
color = R_GetTranslationColormap(TC_DEFAULT, SKINCOLOR_POSNUM_BEST1 + (counter % 6), GTC_CACHE);
}
else if (stplyr->laps >= numlaps || stplyr->exiting)
{
// On the final lap, or already won.
boolean useRedNums = K_IsPlayerLosing(stplyr);
if ((mapheaderinfo[gamemap - 1]->levelflags & LF_SUBTRACTNUM) == LF_SUBTRACTNUM)
{
// Subtracting RED will look BLUE, and vice versa.
useRedNums = !useRedNums;
}
if (useRedNums == true)
{
color = R_GetTranslationColormap(TC_DEFAULT, SKINCOLOR_POSNUM_LOSE1 + (counter % 3), GTC_CACHE);
}
else
{
color = R_GetTranslationColormap(TC_DEFAULT, SKINCOLOR_POSNUM_WIN1 + (counter % 3), GTC_CACHE);
}
}
else
{
if (stplyr == &players[displayplayers[0]] || stplyr == &players[displayplayers[2]]) // If we are P1 or P3...
{
fx = POSI_X;
fy = POSI_Y;
fflags = V_SNAPTOLEFT|V_SNAPTOBOTTOM|V_SPLITSCREEN;
flipdraw = true;
if (num && num >= 10)
fx += W; // this seems dumb, but we need to do this in order for positions above 10 going off screen.
}
else // else, that means we're P2 or P4.
{
fx = POSI2_X;
fy = POSI2_Y;
fflags = V_SNAPTORIGHT|V_SNAPTOBOTTOM|V_SPLITSCREEN;
}
color = R_GetTranslationColormap(TC_DEFAULT, SKINCOLOR_POSNUM, GTC_CACHE);
}
// Special case for 0
if (num <= 0)
{
V_DrawFixedPatch(fx<<FRACBITS, fy<<FRACBITS, scale, addOrSub|V_SLIDEIN|fflags, kp_positionnum[0][0], NULL);
K_DrawKartPositionNumPatch(
0, color,
fx, fy, scale, V_SLIDEIN|V_SPLITSCREEN|fflags
);
return;
}
// Draw the number
while (num)
{
if (win) // 1st place winner? You get rainbows!!
{
localpatch = kp_winnernum[(leveltime % (NUMWINFRAMES*3)) / 3];
}
else if (stplyr->laps >= numlaps || stplyr->exiting) // Check for the final lap, or won
{
boolean useRedNums = K_IsPlayerLosing(stplyr);
/*
*/
if (addOrSub == V_SUBTRACT)
{
// Subtracting RED will look BLUE, and vice versa.
useRedNums = !useRedNums;
}
// Alternate frame every three frames
switch ((leveltime % 9) / 3)
{
case 0:
if (useRedNums == true)
localpatch = kp_positionnum[num % 10][4];
else
localpatch = kp_positionnum[num % 10][1];
break;
case 1:
if (useRedNums == true)
localpatch = kp_positionnum[num % 10][5];
else
localpatch = kp_positionnum[num % 10][2];
break;
case 2:
if (useRedNums == true)
localpatch = kp_positionnum[num % 10][6];
else
localpatch = kp_positionnum[num % 10][3];
break;
default:
localpatch = kp_positionnum[num % 10][0];
break;
}
}
else
{
localpatch = kp_positionnum[num % 10][0];
}
V_DrawFixedPatch(
(fx<<FRACBITS) + ((overtake && flipdraw) ? (SHORT(localpatch->width)*scale/2) : 0),
(fy<<FRACBITS) + ((overtake && flipvdraw) ? (SHORT(localpatch->height)*scale/2) : 0),
scale, addOrSub|V_SLIDEIN|fflags, localpatch, NULL
fx = K_DrawKartPositionNumPatch(
(num % 10), color,
fx, fy, scale, V_SLIDEIN|V_SPLITSCREEN|fflags
);
// ^ if we overtake as p1 or p3 in splitscren, we shift it so that it doesn't go off screen.
// ^ if we overtake as p1 in 2p splits, shift vertically so that this doesn't happen either.
fx -= W;
num /= 10;
}
}
@ -4494,7 +4540,6 @@ static void K_drawDistributionDebugger(void)
kp_sneaker[1],
kp_sneaker[1],
kp_banana[1],
kp_banana[1],
kp_orbinaut[4],
kp_orbinaut[4],
kp_jawz[1]

View file

@ -19,6 +19,8 @@
#define RINGANIM_NUMFRAMES 10
#define RINGANIM_DELAYMAX 5
#define POS_DELAY_TIME 10
void K_AdjustXYWithSnap(INT32 *x, INT32 *y, UINT32 options, INT32 dupx, INT32 dupy);
typedef struct trackingResult_s

View file

@ -305,7 +305,6 @@ void K_RegisterKartStuff(void)
CV_RegisterVar(&cv_dualsneaker);
CV_RegisterVar(&cv_triplesneaker);
CV_RegisterVar(&cv_triplebanana);
CV_RegisterVar(&cv_decabanana);
CV_RegisterVar(&cv_tripleorbinaut);
CV_RegisterVar(&cv_quadorbinaut);
CV_RegisterVar(&cv_dualjawz);
@ -409,7 +408,6 @@ consvar_t *KartItemCVars[NUMKARTRESULTS-1] =
&cv_dualsneaker,
&cv_triplesneaker,
&cv_triplebanana,
&cv_decabanana,
&cv_tripleorbinaut,
&cv_quadorbinaut,
&cv_dualjawz
@ -428,7 +426,7 @@ static UINT8 K_KartItemOddsRace[NUMKARTRESULTS-1][8] =
{ 1, 2, 0, 0, 0, 0, 0, 0 }, // Eggman Monitor
{ 5, 5, 2, 2, 0, 0, 0, 0 }, // Orbinaut
{ 0, 4, 2, 1, 0, 0, 0, 0 }, // Jawz
{ 0, 3, 3, 1, 0, 0, 0, 0 }, // Mine
{ 0, 3, 3, 2, 0, 0, 0, 0 }, // Mine
{ 3, 0, 0, 0, 0, 0, 0, 0 }, // Land Mine
{ 0, 0, 2, 2, 0, 0, 0, 0 }, // Ballhog
{ 0, 0, 0, 0, 0, 2, 4, 0 }, // Self-Propelled Bomb
@ -446,7 +444,6 @@ static UINT8 K_KartItemOddsRace[NUMKARTRESULTS-1][8] =
{ 0, 0, 2, 2, 2, 0, 0, 0 }, // Sneaker x2
{ 0, 0, 0, 0, 4, 4, 4, 0 }, // Sneaker x3
{ 0, 1, 1, 0, 0, 0, 0, 0 }, // Banana x3
{ 0, 0, 0, 1, 0, 0, 0, 0 }, // Banana x10
{ 0, 0, 1, 0, 0, 0, 0, 0 }, // Orbinaut x3
{ 0, 0, 0, 2, 0, 0, 0, 0 }, // Orbinaut x4
{ 0, 0, 1, 2, 1, 0, 0, 0 } // Jawz x2
@ -480,7 +477,6 @@ static UINT8 K_KartItemOddsBattle[NUMKARTRESULTS][2] =
{ 0, 0 }, // Sneaker x2
{ 0, 1 }, // Sneaker x3
{ 0, 0 }, // Banana x3
{ 1, 1 }, // Banana x10
{ 2, 0 }, // Orbinaut x3
{ 1, 1 }, // Orbinaut x4
{ 5, 1 } // Jawz x2
@ -516,7 +512,6 @@ static UINT8 K_KartItemOddsSpecial[NUMKARTRESULTS-1][4] =
{ 0, 1, 1, 0 }, // Sneaker x2
{ 0, 0, 1, 1 }, // Sneaker x3
{ 0, 0, 0, 0 }, // Banana x3
{ 0, 0, 0, 0 }, // Banana x10
{ 0, 1, 1, 0 }, // Orbinaut x3
{ 0, 0, 1, 1 }, // Orbinaut x4
{ 0, 0, 1, 1 } // Jawz x2
@ -577,7 +572,6 @@ SINT8 K_ItemResultToType(SINT8 getitem)
return KITEM_SNEAKER;
case KRITEM_TRIPLEBANANA:
case KRITEM_TENFOLDBANANA:
return KITEM_BANANA;
case KRITEM_TRIPLEORBINAUT:
@ -615,9 +609,6 @@ UINT8 K_ItemResultToAmount(SINT8 getitem)
case KITEM_BALLHOG: // Not a special result, but has a special amount
return 5;
case KRITEM_TENFOLDBANANA:
return 10;
default:
return 1;
}
@ -891,7 +882,6 @@ INT32 K_KartGetItemOdds(
break;
case KRITEM_TRIPLEBANANA:
case KRITEM_TENFOLDBANANA:
powerItem = true;
notNearEnd = true;
break;
@ -3430,7 +3420,7 @@ boolean K_TripwirePass(player_t *player)
boolean K_MovingHorizontally(mobj_t *mobj)
{
return (P_AproxDistance(mobj->momx, mobj->momy) / 5 > abs(mobj->momz));
return (P_AproxDistance(mobj->momx, mobj->momy) / 4 > abs(mobj->momz));
}
boolean K_WaterRun(mobj_t *mobj)
@ -6133,6 +6123,7 @@ mobj_t *K_ThrowKartItem(player_t *player, boolean missile, mobjtype_t mapthing,
{
// Shoot forward
mo = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z + player->mo->height/2, mapthing);
mo->angle = player->mo->angle;
// These are really weird so let's make it a very specific case to make SURE it works...
if (player->mo->eflags & MFE_VERTICALFLIP)
@ -6147,7 +6138,6 @@ mobj_t *K_ThrowKartItem(player_t *player, boolean missile, mobjtype_t mapthing,
S_StartSound(player->mo, mo->info->seesound);
if (mo)
{
angle_t fa = player->mo->angle>>ANGLETOFINESHIFT;
fixed_t HEIGHT = ((20 + (dir*10)) * FRACUNIT) + (FixedDiv(player->mo->momz, mapobjectscale)*P_MobjFlip(player->mo)); // Also intentionally not player scale
@ -6155,14 +6145,20 @@ mobj_t *K_ThrowKartItem(player_t *player, boolean missile, mobjtype_t mapthing,
P_SetObjectMomZ(mo, HEIGHT, false);
mo->momx = player->mo->momx + FixedMul(FINECOSINE(fa), PROJSPEED*dir);
mo->momy = player->mo->momy + FixedMul(FINESINE(fa), PROJSPEED*dir);
}
mo->extravalue2 = dir;
mo->extravalue2 = dir;
if (mo->eflags & MFE_UNDERWATER)
mo->momz = (117 * mo->momz) / 200;
if (mo->eflags & MFE_UNDERWATER)
mo->momz = (117 * mo->momz) / 200;
P_SetScale(mo, finalscale);
mo->destscale = finalscale;
P_SetScale(mo, finalscale);
mo->destscale = finalscale;
if (mapthing == MT_BANANA)
{
mo->angle = FixedAngle(P_RandomRange(PR_DECORATION, -180, 180) << FRACBITS);
mo->rollangle = FixedAngle(P_RandomRange(PR_DECORATION, -180, 180) << FRACBITS);
}
// this is the small graphic effect that plops in you when you throw an item:
@ -6247,6 +6243,8 @@ mobj_t *K_ThrowKartItem(player_t *player, boolean missile, mobjtype_t mapthing,
if (player->mo->eflags & MFE_VERTICALFLIP)
mo->eflags |= MFE_VERTICALFLIP;
mo->angle = newangle;
if (mapthing == MT_SSMINE)
mo->extravalue1 = 49; // Pads the start-up length from 21 frames to a full 2 seconds
else if (mapthing == MT_BUBBLESHIELDTRAP)
@ -6568,6 +6566,7 @@ void K_DoPogoSpring(mobj_t *mo, fixed_t vertispeed, UINT8 sound)
mo->player->tricktime = 0; // Reset post-hitlag timer
// Setup the boost for potential upwards trick, at worse, make it your regular max speed. (boost = curr speed*1.25)
mo->player->trickboostpower = max(FixedDiv(mo->player->speed, K_GetKartSpeed(mo->player, false, false)) - FRACUNIT, 0)*125/100;
mo->player->trickboostpower = FixedDiv(mo->player->trickboostpower, K_GrowShrinkSpeedMul(mo->player));
//CONS_Printf("Got boost: %d%\n", mo->player->trickboostpower*100 / FRACUNIT);
}
@ -6662,7 +6661,7 @@ killnext:
S_StartSound(banana, banana->info->deathsound);
P_KillMobj(banana, inflictor, source, DMG_NORMAL);
P_SetObjectMomZ(banana, 8*FRACUNIT, false);
P_SetObjectMomZ(banana, 24*FRACUNIT, false);
if (inflictor)
P_InstaThrust(banana, R_PointToAngle2(inflictor->x, inflictor->y, banana->x, banana->y)+ANGLE_90, 16*FRACUNIT);
}
@ -7063,7 +7062,7 @@ void K_DropRocketSneaker(player_t *player)
flingangle = ANG60;
S_StartSound(shoe, shoe->info->deathsound);
P_SetObjectMomZ(shoe, 8*FRACUNIT, false);
P_SetObjectMomZ(shoe, 24*FRACUNIT, false);
P_InstaThrust(shoe, R_PointToAngle2(shoe->target->x, shoe->target->y, shoe->x, shoe->y)+flingangle, 16*FRACUNIT);
shoe->momx += shoe->target->momx;
shoe->momy += shoe->target->momy;
@ -9865,8 +9864,16 @@ void K_KartUpdatePosition(player_t *player)
if (leveltime < starttime || oldposition == 0)
oldposition = position;
if (oldposition != position) // Changed places?
player->positiondelay = 10; // Position number growth
if (position != oldposition) // Changed places?
{
if (position < oldposition && P_IsDisplayPlayer(player) == true)
{
// Play sound when getting closer to 1st.
S_StartSound(player->mo, sfx_mbs41);
}
player->positiondelay = POS_DELAY_TIME + 4; // Position number growth
}
/* except in FREE PLAY */
if (player->curshield == KSHIELD_TOP &&
@ -11361,9 +11368,10 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
{
const angle_t lr = ANGLE_45;
fixed_t momz = FixedDiv(player->mo->momz, mapobjectscale); // bring momz back to scale...
fixed_t invertscale = FixedDiv(FRACUNIT, K_GrowShrinkSpeedMul(player));
fixed_t speedmult = max(0, FRACUNIT - abs(momz)/TRICKMOMZRAMP); // TRICKMOMZRAMP momz is minimum speed (Should be 20)
fixed_t basespeed = K_GetKartSpeed(player, false, false); // at WORSE, keep your normal speed when tricking.
fixed_t speed = FixedMul(speedmult, P_AproxDistance(player->mo->momx, player->mo->momy));
fixed_t basespeed = FixedMul(invertscale, K_GetKartSpeed(player, false, false)); // at WORSE, keep your normal speed when tricking.
fixed_t speed = FixedMul(invertscale, FixedMul(speedmult, P_AproxDistance(player->mo->momx, player->mo->momy)));
K_trickPanelTimingVisual(player, momz);
@ -11450,14 +11458,12 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
}
else if (cmd->throwdir < 0)
{
boolean relative = true;
player->mo->momx /= 3;
player->mo->momy /= 3;
if (player->mo->momz * P_MobjFlip(player->mo) <= 0)
{
relative = false;
player->mo->momz = 0; // relative = false;
}
// Calculate speed boost decay:
@ -11466,7 +11472,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
player->trickboostdecay = min(TICRATE*3/4, abs(momz/FRACUNIT));
//CONS_Printf("decay: %d\n", player->trickboostdecay);
P_SetObjectMomZ(player->mo, 48*FRACUNIT, relative);
player->mo->momz += P_MobjFlip(player->mo)*48*mapobjectscale;
player->trickpanel = 4;
}
}

View file

@ -410,6 +410,8 @@ typedef enum
{
mpause_addons = 0,
mpause_switchmap,
mpause_restartmap,
mpause_tryagain,
#ifdef HAVE_DISCORDRPC
mpause_discordrequests,
#endif
@ -599,6 +601,7 @@ typedef enum
CSSTEP_CHARS,
CSSTEP_ALTS,
CSSTEP_COLORS,
CSSTEP_FOLLOWERCATEGORY,
CSSTEP_FOLLOWER,
CSSTEP_FOLLOWERCOLORS,
CSSTEP_READY
@ -614,6 +617,7 @@ typedef struct setup_player_s
UINT8 delay;
UINT16 color;
UINT8 mdepth;
boolean hitlag;
// Hack, save player 1's original device even if they init charsel with keyboard.
// If they play ALONE, allow them to retain that original device, otherwise, ignore this.
@ -622,7 +626,8 @@ typedef struct setup_player_s
UINT8 changeselect;
INT32 followern;
INT16 followercategory;
INT16 followern;
UINT16 followercolor;
tic_t follower_tics;
tic_t follower_timer;
@ -975,6 +980,8 @@ extern consvar_t cv_dummymenuplayer;
extern consvar_t cv_dummyspectator;
// Bunch of funny functions for the pause menu...~
void M_RestartMap(INT32 choice); // Restart level (MP)
void M_TryAgain(INT32 choice); // Try again (SP)
void M_ConfirmSpectate(INT32 choice); // Spectate confirm when you're alone
void M_ConfirmEnterGame(INT32 choice); // Enter game confirm when you're alone
void M_ConfirmSpectateChange(INT32 choice); // Splitscreen spectate/play menu func

View file

@ -1071,7 +1071,6 @@ menuitem_t OPTIONS_GameplayItems[] =
{IT_KEYHANDLER | IT_NOTHING, NULL, "Bananas", NULL, {.routine = M_HandleItemToggles}, KITEM_BANANA, 0},
{IT_KEYHANDLER | IT_NOTHING, NULL, "Bananas x3", NULL, {.routine = M_HandleItemToggles}, KRITEM_TRIPLEBANANA, 0},
{IT_KEYHANDLER | IT_NOTHING, NULL, "Bananas x10", NULL, {.routine = M_HandleItemToggles}, KRITEM_TENFOLDBANANA, 0},
{IT_KEYHANDLER | IT_NOTHING, NULL, "Proximity Mines", NULL, {.routine = M_HandleItemToggles}, KITEM_MINE, 0},
{IT_KEYHANDLER | IT_NOTHING, NULL, "Orbinauts", NULL, {.routine = M_HandleItemToggles}, KITEM_ORBINAUT, 0},
@ -1594,6 +1593,12 @@ menuitem_t PAUSE_Main[] =
{IT_STRING | IT_SUBMENU, "CHANGE MAP", "M_ICOMAP",
NULL, {.submenu = &PAUSE_GamemodesDef}, 0, 0},
{IT_STRING | IT_CALL, "RESTART MAP", "M_ICORE",
NULL, {.routine = M_RestartMap}, 0, 0},
{IT_STRING | IT_CALL, "TRY AGAIN", "M_ICORE",
NULL, {.routine = M_TryAgain}, 0, 0},
#ifdef HAVE_DISCORDRPC
{IT_STRING | IT_CALL, "DISCORD REQUESTS", "M_ICODIS",
NULL, {NULL}, 0, 0},

View file

@ -969,7 +969,8 @@ static void M_DrawCharSelectCircle(setup_player_t *p, INT16 x, INT16 y)
{
angle_t angamt = ANGLE_MAX;
UINT16 i, numoptions = 0;
UINT16 l = 0, r = 0;
INT16 l = 0, r = 0;
INT16 subtractcheck;
switch (p->mdepth)
{
@ -979,8 +980,11 @@ static void M_DrawCharSelectCircle(setup_player_t *p, INT16 x, INT16 y)
case CSSTEP_COLORS:
numoptions = nummenucolors;
break;
case CSSTEP_FOLLOWERCATEGORY:
numoptions = numfollowercategories+1;
break;
case CSSTEP_FOLLOWER:
numoptions = numfollowers+1;
numoptions = followercategories[p->followercategory].numincategory;
break;
case CSSTEP_FOLLOWERCOLORS:
numoptions = nummenucolors+2;
@ -994,17 +998,19 @@ static void M_DrawCharSelectCircle(setup_player_t *p, INT16 x, INT16 y)
return;
}
subtractcheck = 1 ^ (numoptions & 1);
angamt /= numoptions;
for (i = 0; i < numoptions; i++)
{
fixed_t cx = x << FRACBITS, cy = y << FRACBITS;
boolean subtract = (i & 1);
boolean subtract = (i & 1) == subtractcheck;
angle_t ang = ((i+1)/2) * angamt;
patch_t *patch = NULL;
UINT8 *colormap = NULL;
fixed_t radius = 28<<FRACBITS;
UINT16 n = 0;
INT16 n = 0;
switch (p->mdepth)
{
@ -1017,7 +1023,7 @@ static void M_DrawCharSelectCircle(setup_player_t *p, INT16 x, INT16 y)
n -= ((i+1)/2);
else
n += ((i+1)/2);
n %= numoptions;
n = (n + numoptions) % numoptions;
skin = setup_chargrid[p->gridx][p->gridy].skinlist[n];
patch = faceprefix[skin][FACE_RANK];
@ -1061,16 +1067,16 @@ static void M_DrawCharSelectCircle(setup_player_t *p, INT16 x, INT16 y)
break;
}
case CSSTEP_FOLLOWER:
case CSSTEP_FOLLOWERCATEGORY:
{
follower_t *fl = NULL;
followercategory_t *fc = NULL;
n = (p->followern + 1) + numoptions/2;
n = (p->followercategory + 1) + numoptions/2;
if (subtract)
n -= ((i+1)/2);
else
n += ((i+1)/2);
n %= numoptions;
n = (n + numoptions) % numoptions;
if (n == 0)
{
@ -1078,11 +1084,71 @@ static void M_DrawCharSelectCircle(setup_player_t *p, INT16 x, INT16 y)
}
else
{
fl = &followers[n - 1];
fc = &followercategories[n - 1];
patch = W_CachePatchName(fc->icon, PU_CACHE);
}
radius = 24<<FRACBITS;
cx -= (SHORT(patch->width) << FRACBITS) >> 1;
cy -= (SHORT(patch->height) << FRACBITS) >> 1;
break;
}
case CSSTEP_FOLLOWER:
{
follower_t *fl = NULL;
INT16 startfollowern = p->followern;
if (i == 0)
{
n = p->followern;
r = (numoptions+1)/2;
while (r)
{
n--;
if (n < 0)
n = numfollowers-1;
if (n == startfollowern)
break;
if (followers[n].category == p->followercategory)
r--;
}
l = r = n;
}
else if (subtract)
{
do
{
l--;
if (l < 0)
l = numfollowers-1;
if (l == startfollowern)
break;
}
while (followers[l].category != p->followercategory);
n = l;
}
else
{
do
{
r++;
if (r >= numfollowers)
r = 0;
if (r == startfollowern)
break;
}
while (followers[r].category != p->followercategory);
n = r;
}
{
fl = &followers[n];
patch = W_CachePatchName(fl->icon, PU_CACHE);
colormap = R_GetTranslationColormap(TC_DEFAULT,
K_GetEffectiveFollowerColor(p->followercolor, p->color),
K_GetEffectiveFollowerColor(fl->defaultcolor, p->color),
GTC_MENUCACHE
);
}
@ -1142,7 +1208,12 @@ static void M_DrawCharSelectCircle(setup_player_t *p, INT16 x, INT16 y)
ang = (signed)(ang - (angamt/2));
if (p->rotate)
ang = (signed)(ang + ((angamt / CSROTATETICS) * p->rotate));
{
SINT8 rotate = p->rotate;
if ((p->hitlag == true) && (setup_animcounter & 1))
rotate = -rotate;
ang = (signed)(ang + ((angamt / CSROTATETICS) * rotate));
}
cx += FixedMul(radius, FINECOSINE(ang >> ANGLETOFINESHIFT));
cy -= FixedMul(radius, FINESINE(ang >> ANGLETOFINESHIFT)) / 3;
@ -1154,41 +1225,40 @@ static void M_DrawCharSelectCircle(setup_player_t *p, INT16 x, INT16 y)
}
// returns false if the character couldn't be rendered
static boolean M_DrawCharacterSprite(INT16 x, INT16 y, INT16 skin, INT32 addflags, UINT8 *colormap)
static boolean M_DrawCharacterSprite(INT16 x, INT16 y, INT16 skin, boolean charflip, boolean animate, INT32 addflags, UINT8 *colormap)
{
UINT8 spr;
spritedef_t *sprdef;
spriteframe_t *sprframe;
patch_t *sprpatch;
UINT8 rotation = (charflip ? 1 : 7);
UINT32 frame = animate ? setup_animcounter : 0;
UINT32 flags = 0;
UINT32 frame;
spr = P_GetSkinSprite2(&skins[skin], SPR2_FSTN, NULL);
spr = P_GetSkinSprite2(&skins[skin], SPR2_STIN, NULL);
sprdef = &skins[skin].sprites[spr];
if (!sprdef->numframes) // No frames ??
return false; // Can't render!
frame = states[S_KART_FAST].frame & FF_FRAMEMASK;
if (frame >= sprdef->numframes) // Walking animation missing
frame = 0; // Try to use standing frame
frame %= sprdef->numframes;
sprframe = &sprdef->spriteframes[frame];
sprpatch = W_CachePatchNum(sprframe->lumppat[1], PU_CACHE);
sprpatch = W_CachePatchNum(sprframe->lumppat[rotation], PU_CACHE);
if (sprframe->flip & 1) // Only for first sprite
flags |= V_FLIP; // This sprite is left/right flipped!
if (sprframe->flip & (1<<rotation)) // Only for first sprite
{
addflags ^= V_FLIP; // This sprite is left/right flipped!
}
if (skins[skin].flags & SF_HIRES)
{
V_DrawFixedPatch(x<<FRACBITS,
y<<FRACBITS,
skins[skin].highresscale,
flags, sprpatch, colormap);
addflags, sprpatch, colormap);
}
else
V_DrawMappedPatch(x, y, addflags|flags, sprpatch, colormap);
V_DrawMappedPatch(x, y, addflags, sprpatch, colormap);
return true;
}
@ -1196,7 +1266,7 @@ static boolean M_DrawCharacterSprite(INT16 x, INT16 y, INT16 skin, INT32 addflag
// Returns false is the follower shouldn't be rendered.
// 'num' can be used to directly specify the follower number, but doing this will not animate it.
// if a setup_player_t is specified instead, its data will be used to animate the follower sprite.
static boolean M_DrawFollowerSprite(INT16 x, INT16 y, INT32 num, INT32 addflags, UINT16 color, setup_player_t *p)
static boolean M_DrawFollowerSprite(INT16 x, INT16 y, INT32 num, boolean charflip, INT32 addflags, UINT16 color, setup_player_t *p)
{
spritedef_t *sprdef;
@ -1207,6 +1277,7 @@ static boolean M_DrawFollowerSprite(INT16 x, INT16 y, INT32 num, INT32 addflags,
UINT32 useframe;
follower_t fl;
UINT8 *colormap = NULL;
UINT8 rotation = (charflip ? 1 : 7);
if (p != NULL)
followernum = p->followern;
@ -1238,14 +1309,11 @@ static boolean M_DrawFollowerSprite(INT16 x, INT16 y, INT32 num, INT32 addflags,
useframe = 0; // frame doesn't exist, we went beyond it... what?
sprframe = &sprdef->spriteframes[useframe];
patch = W_CachePatchNum(sprframe->lumppat[1], PU_CACHE);
patch = W_CachePatchNum(sprframe->lumppat[rotation], PU_CACHE);
if (sprframe->flip & 2) // Only for first sprite
if (sprframe->flip & (1<<rotation)) // Only for first sprite
{
if (addflags & V_FLIP)
addflags &= ~V_FLIP;
else
addflags |= V_FLIP; // This sprite is left/right flipped!
addflags ^= V_FLIP; // This sprite is left/right flipped!
}
fixed_t sine = 0;
@ -1253,35 +1321,33 @@ static boolean M_DrawFollowerSprite(INT16 x, INT16 y, INT32 num, INT32 addflags,
if (p != NULL)
{
sine = FixedMul(fl.bobamp, FINESINE(((FixedMul(4 * M_TAU_FIXED, fl.bobspeed) * p->follower_timer)>>ANGLETOFINESHIFT) & FINEMASK));
color = K_GetEffectiveFollowerColor(p->followercolor, p->color);
color = K_GetEffectiveFollowerColor(
(p->mdepth < CSSTEP_FOLLOWERCOLORS) ? fl.defaultcolor : p->followercolor,
p->color);
}
colormap = R_GetTranslationColormap(TC_DEFAULT, color, GTC_MENUCACHE);
V_DrawFixedPatch((x)*FRACUNIT, ((y-12)*FRACUNIT) + sine - fl.zoffs, fl.scale, addflags, patch, colormap);
V_DrawFixedPatch((x*FRACUNIT), ((y-12)*FRACUNIT) + sine, fl.scale, addflags, patch, colormap);
return true;
}
static void M_DrawCharSelectSprite(UINT8 num, INT16 x, INT16 y)
static void M_DrawCharSelectSprite(UINT8 num, INT16 x, INT16 y, boolean charflip)
{
setup_player_t *p = &setup_player[num];
UINT8 cnum = p->clonenum;
UINT8 color;
UINT8 *colormap;
// for p1 alone don't try to preview things on pages that don't exist lol.
if (p->mdepth == CSSTEP_CHARS && setup_numplayers == 1)
cnum = setup_page;
if (p->skin < 0)
return;
INT16 skin = setup_chargrid[p->gridx][p->gridy].skinlist[cnum];
UINT8 color = p->color;
UINT8 *colormap = R_GetTranslationColormap(skin, color, GTC_MENUCACHE);
INT32 flags = 0;
if (p->mdepth < CSSTEP_COLORS)
color = skins[p->skin].prefcolor;
else
color = p->color;
colormap = R_GetTranslationColormap(p->skin, color, GTC_MENUCACHE);
// Flip for left-side players
if (!(num & 1))
flags ^= V_FLIP;
if (skin >= 0)
M_DrawCharacterSprite(x, y, skin, flags, colormap);
M_DrawCharacterSprite(x, y, p->skin, charflip, (p->mdepth == CSSTEP_READY), 0, colormap);
}
static void M_DrawCharSelectPreview(UINT8 num)
@ -1289,6 +1355,7 @@ static void M_DrawCharSelectPreview(UINT8 num)
INT16 x = 11, y = 5;
char letter = 'A' + num;
setup_player_t *p = &setup_player[num];
boolean charflip = !!(num & 1);
if (num & 1)
x += 233;
@ -1300,28 +1367,10 @@ static void M_DrawCharSelectPreview(UINT8 num)
if (p->mdepth >= CSSTEP_CHARS)
{
M_DrawCharSelectSprite(num, x+32, y+75);
if (p->mdepth >= CSSTEP_FOLLOWER)
{
M_DrawFollowerSprite(x+16, y+75, -1, !(num & 1) ? V_FLIP : 0, 0, p);
}
M_DrawCharSelectSprite(num, x+32, y+75, charflip);
M_DrawCharSelectCircle(p, x+32, y+64);
}
if ((setup_animcounter/10) & 1 && gamestate == GS_MENU) // Not drawn outside of GS_MENU.
{
if (p->mdepth == CSSTEP_NONE && num == setup_numplayers)
{
V_DrawScaledPatch(x+1, y+36, 0, W_CachePatchName("4PSTART", PU_CACHE));
}
else if (p->mdepth >= CSSTEP_READY)
{
V_DrawScaledPatch(x+1, y+36, 0, W_CachePatchName("4PREADY", PU_CACHE));
}
}
V_DrawScaledPatch(x+9, y+2, 0, W_CachePatchName("FILEBACK", PU_CACHE));
V_DrawScaledPatch(x, y+2, 0, W_CachePatchName(va("CHARSEL%c", letter), PU_CACHE));
if (p->mdepth > CSSTEP_PROFILE)
@ -1334,6 +1383,23 @@ static void M_DrawCharSelectPreview(UINT8 num)
V_DrawFileString(x+16, y+2, 0, "PLAYER");
}
if (p->mdepth >= CSSTEP_FOLLOWER)
{
M_DrawFollowerSprite(x+32+((charflip ? 1 : -1)*16), y+75, -1, charflip, 0, 0, p);
}
if ((setup_animcounter/10) & 1 && gamestate == GS_MENU) // Not drawn outside of GS_MENU.
{
if (p->mdepth == CSSTEP_NONE && num == setup_numplayers)
{
V_DrawScaledPatch(x+1, y+36, 0, W_CachePatchName("4PSTART", PU_CACHE));
}
else if (p->mdepth >= CSSTEP_READY)
{
V_DrawScaledPatch(x+1, y+36, 0, W_CachePatchName("4PREADY", PU_CACHE));
}
}
// Profile selection
if (p->mdepth == CSSTEP_PROFILE)
{
@ -1549,39 +1615,38 @@ static void M_DrawProfileCard(INT32 x, INT32 y, boolean greyedout, profile_t *p)
// check what setup_player is doing in priority.
if (sp->mdepth >= CSSTEP_CHARS)
{
skinnum = setup_chargrid[sp->gridx][sp->gridy].skinlist[sp->clonenum];
if (skinnum >= 0)
{
if (M_DrawCharacterSprite(x-22, y+119, skinnum, V_FLIP, colormap))
if (M_DrawCharacterSprite(x-22, y+119, skinnum, false, false, 0, colormap))
V_DrawMappedPatch(x+14, y+66, 0, faceprefix[skinnum][FACE_RANK], colormap);
if (sp->mdepth >= CSSTEP_FOLLOWER)
{
if (M_DrawFollowerSprite(x-44 +12, y+119, 0, V_FLIP, 0, sp))
{
UINT16 col = K_GetEffectiveFollowerColor(sp->followercolor, sp->color);;
patch_t *ico = W_CachePatchName(followers[sp->followern].icon, PU_CACHE);
UINT8 *fcolormap;
fcolormap = R_GetTranslationColormap(TC_DEFAULT, col, GTC_MENUCACHE);
V_DrawMappedPatch(x+14+18, y+66, 0, ico, fcolormap);
}
}
}
M_DrawCharSelectCircle(sp, x-22, y+104);
if (sp->mdepth >= CSSTEP_FOLLOWER)
{
if (M_DrawFollowerSprite(x-22 - 16, y+119, 0, false, 0, 0, sp))
{
UINT16 col = K_GetEffectiveFollowerColor(sp->followercolor, sp->color);;
patch_t *ico = W_CachePatchName(followers[sp->followern].icon, PU_CACHE);
UINT8 *fcolormap;
fcolormap = R_GetTranslationColormap(TC_DEFAULT, col, GTC_MENUCACHE);
V_DrawMappedPatch(x+14+18, y+66, 0, ico, fcolormap);
}
}
}
else if (skinnum > -1) // otherwise, read from profile.
{
UINT16 col = K_GetEffectiveFollowerColor(p->followercolor, p->color);;
UINT8 fln = K_FollowerAvailable(p->follower);
if (M_DrawCharacterSprite(x-22, y+119, skinnum, V_FLIP, colormap))
if (M_DrawCharacterSprite(x-22, y+119, skinnum, false, false, 0, colormap))
V_DrawMappedPatch(x+14, y+66, 0, faceprefix[skinnum][FACE_RANK], colormap);
if (M_DrawFollowerSprite(x-44 +12, y+119, fln, V_FLIP, col, NULL))
if (M_DrawFollowerSprite(x-22 - 16, y+119, fln, false, 0, col, NULL))
{
patch_t *ico = W_CachePatchName(followers[fln].icon, PU_CACHE);
UINT8 *fcolormap = R_GetTranslationColormap(TC_DEFAULT, col, GTC_MENUCACHE);
@ -3594,19 +3659,29 @@ void M_DrawPause(void)
{
case IT_STRING:
{
char iconame[9]; // 8 chars + \0
patch_t *pp;
if (i == itemOn)
{
strcpy(iconame, currentMenu->menuitems[i].tooltip);
iconame[7] = '2'; // Yes this is a stupid hack. Replace the last character with a 2 when we're selecting this graphic.
if (i == mpause_restartmap || i == mpause_tryagain)
{
pp = W_CachePatchName(
va("M_ICOR2%c", ('A'+(pausemenu.ticker & 1))),
PU_CACHE);
}
else
{
char iconame[9]; // 8 chars + \0
strcpy(iconame, currentMenu->menuitems[i].tooltip);
iconame[7] = '2'; // Yes this is a stupid hack. Replace the last character with a 2 when we're selecting this graphic.
pp = W_CachePatchName(iconame, PU_CACHE);
pp = W_CachePatchName(iconame, PU_CACHE);
}
}
else
{
pp = W_CachePatchName(currentMenu->menuitems[i].tooltip, PU_CACHE);
}
// 294 - 261 = 33
// We need to move 33 px in 50 tics which means we move 33/50 = 0.66 px every tic = 2/3 of the offset.

View file

@ -357,7 +357,7 @@ static void M_EraseDataResponse(INT32 ch)
void M_EraseData(INT32 choice)
{
const char *eschoice, *esstr = M_GetText("Are you sure you want to erase\n%s?\n\n(Press A to confirm)\n");
const char *eschoice, *esstr = M_GetText("Are you sure you want to erase\n%s?\n\nPress (A) to confirm or (B) to cancel\n");
optionsmenu.erasecontext = (UINT8)choice;
@ -1517,7 +1517,7 @@ static void M_HandleMenuInput(void)
{
if (((currentMenu->menuitems[itemOn].status & IT_CALLTYPE) & IT_CALL_NOTMODIFIED) && majormods)
{
M_StartMessage(M_GetText("This cannot be done with complex addons\nor in a cheated game.\n\n(Press a key)\n"), NULL, MM_NOTHING);
M_StartMessage(M_GetText("This cannot be done with complex addons\nor in a cheated game.\n\nPress (B)\n"), NULL, MM_NOTHING);
return;
}
}
@ -1583,6 +1583,12 @@ void M_Ticker(void)
{
INT32 i;
if (!menuactive)
{
noFurtherInput = false;
return;
}
if (menutransition.tics != 0 || menutransition.dest != 0)
{
noFurtherInput = true;
@ -2035,7 +2041,7 @@ void M_QuitSRB2(INT32 choice)
// We pick index 0 which is language sensitive, or one at random,
// between 1 and maximum number.
(void)choice;
M_StartMessage("Are you sure you want to quit playing?\n\n(Press A to exit)", FUNCPTRCAST(M_QuitResponse), MM_YESNO);
M_StartMessage("Are you sure you want to quit playing?\n\nPress (A) to confirm or (B) to cancel", FUNCPTRCAST(M_QuitResponse), MM_YESNO);
}
// =========
@ -2063,6 +2069,12 @@ static void M_SetupProfileGridPos(setup_player_t *p)
// While we're here, read follower values.
p->followern = K_FollowerAvailable(pr->follower);
if (p->followern < 0 || p->followern >= numfollowers || followers[p->followern].category >= numfollowercategories)
p->followercategory = -1;
else
p->followercategory = followers[p->followern].category;
p->followercolor = pr->followercolor;
// Now position the grid for skin
@ -2150,6 +2162,7 @@ void M_CharacterSelectInit(void)
{
// Default to no follower / match colour.
setup_player[i].followern = -1;
setup_player[i].followercategory = -1;
setup_player[i].followercolor = FOLLOWERCOLOR_MATCH;
// Set default selected profile to the last used profile for each player:
@ -2486,6 +2499,16 @@ static boolean M_HandleCSelectProfile(setup_player_t *p, UINT8 num)
S_StartSound(NULL, sfx_s3k63);
}
else if (M_MenuExtraPressed(num))
{
UINT8 yourprofile = min(cv_lastprofile[realnum].value, PR_GetNumProfiles());
if (p->profilen == yourprofile)
p->profilen = PROFILE_GUEST;
else
p->profilen = yourprofile;
S_StartSound(NULL, sfx_s3k7b); //sfx_s3kc3s
M_SetMenuDelay(num);
}
return false;
@ -2578,6 +2601,15 @@ static boolean M_HandleCharacterGrid(setup_player_t *p, UINT8 num)
S_StartSound(NULL, sfx_s3k5b);
M_SetMenuDelay(num);
}
else if (M_MenuExtraPressed(num))
{
p->gridx /= 3;
p->gridx = (3*p->gridx) + 1;
p->gridy /= 3;
p->gridy = (3*p->gridy) + 1;
S_StartSound(NULL, sfx_s3k7b); //sfx_s3kc3s
M_SetMenuDelay(num);
}
// try to set the clone num to the page # if possible.
p->clonenum = setup_page;
@ -2692,6 +2724,14 @@ static void M_HandleCharRotate(setup_player_t *p, UINT8 num)
S_StartSound(NULL, sfx_s3k5b);
M_SetMenuDelay(num);
}
else if (M_MenuExtraPressed(num))
{
p->clonenum = 0;
p->rotate = CSROTATETICS;
p->hitlag = true;
S_StartSound(NULL, sfx_s3k7b); //sfx_s3kc3s
M_SetMenuDelay(num);
}
}
static void M_HandleColorRotate(setup_player_t *p, UINT8 num)
@ -2716,8 +2756,7 @@ static void M_HandleColorRotate(setup_player_t *p, UINT8 num)
if (M_MenuConfirmPressed(num) /*|| M_MenuButtonPressed(num, MBT_START)*/)
{
p->mdepth = CSSTEP_FOLLOWER;
M_GetFollowerState(p);
p->mdepth = CSSTEP_FOLLOWERCATEGORY;
S_StartSound(NULL, sfx_s3k63);
M_SetMenuDelay(num);
}
@ -2734,6 +2773,17 @@ static void M_HandleColorRotate(setup_player_t *p, UINT8 num)
S_StartSound(NULL, sfx_s3k5b);
M_SetMenuDelay(num);
}
else if (M_MenuExtraPressed(num))
{
if (p->skin >= 0)
{
p->color = skins[p->skin].prefcolor;
p->rotate = CSROTATETICS;
p->hitlag = true;
S_StartSound(NULL, sfx_s3k7b); //sfx_s3kc3s
M_SetMenuDelay(num);
}
}
}
static void M_AnimateFollower(setup_player_t *p)
@ -2764,16 +2814,103 @@ static void M_AnimateFollower(setup_player_t *p)
p->follower_timer++;
}
static void M_HandleFollowerRotate(setup_player_t *p, UINT8 num)
static void M_HandleFollowerCategoryRotate(setup_player_t *p, UINT8 num)
{
if (cv_splitdevice.value)
num = 0;
if (menucmd[num].dpad_lr > 0)
{
p->followern++;
if (p->followern >= numfollowers)
p->followercategory++;
if (p->followercategory >= numfollowercategories)
p->followercategory = -1;
p->rotate = CSROTATETICS;
p->delay = CSROTATETICS;
S_StartSound(NULL, sfx_s3kc3s);
}
else if (menucmd[num].dpad_lr < 0)
{
p->followercategory--;
if (p->followercategory < -1)
p->followercategory = numfollowercategories-1;
p->rotate = -CSROTATETICS;
p->delay = CSROTATETICS;
S_StartSound(NULL, sfx_s3kc3s);
}
if (M_MenuConfirmPressed(num) /*|| M_MenuButtonPressed(num, MBT_START)*/)
{
if (p->followercategory < 0)
{
p->followern = -1;
p->mdepth = CSSTEP_READY;
p->delay = TICRATE;
M_SetupReadyExplosions(p);
S_StartSound(NULL, sfx_s3k4e);
}
else
{
if (p->followern < 0 || followers[p->followern].category != p->followercategory)
{
p->followern = 0;
while (p->followern < numfollowers && followers[p->followern].category != p->followercategory)
p->followern++;
}
if (p->followern >= numfollowers)
{
p->followern = -1;
S_StartSound(NULL, sfx_s3kb2);
}
else
{
M_GetFollowerState(p);
p->mdepth = CSSTEP_FOLLOWER;
S_StartSound(NULL, sfx_s3k63);
}
}
M_SetMenuDelay(num);
}
else if (M_MenuBackPressed(num))
{
p->mdepth = CSSTEP_COLORS;
S_StartSound(NULL, sfx_s3k5b);
M_SetMenuDelay(num);
}
else if (M_MenuExtraPressed(num))
{
if (p->followercategory >= 0 || p->followern < 0 || p->followern >= numfollowers || followers[p->followern].category >= numfollowercategories)
p->followercategory = -1;
else
p->followercategory = followers[p->followern].category;
p->rotate = CSROTATETICS;
p->hitlag = true;
S_StartSound(NULL, sfx_s3k7b); //sfx_s3kc3s
M_SetMenuDelay(num);
}
}
static void M_HandleFollowerRotate(setup_player_t *p, UINT8 num)
{
INT16 startfollowern = p->followern;
if (cv_splitdevice.value)
num = 0;
if (menucmd[num].dpad_lr > 0)
{
do
{
p->followern++;
if (p->followern >= numfollowers)
p->followern = 0;
if (p->followern == startfollowern)
break;
}
while (followers[p->followern].category != p->followercategory);
M_GetFollowerState(p);
@ -2783,9 +2920,17 @@ static void M_HandleFollowerRotate(setup_player_t *p, UINT8 num)
}
else if (menucmd[num].dpad_lr < 0)
{
p->followern--;
if (p->followern < -1)
p->followern = numfollowers-1;
do
{
p->followern--;
if (p->followern < 0)
p->followern = numfollowers-1;
if (p->followern == startfollowern)
break;
}
while (followers[p->followern].category != p->followercategory);
M_GetFollowerState(p);
p->rotate = -CSROTATETICS;
p->delay = CSROTATETICS;
@ -2811,10 +2956,19 @@ static void M_HandleFollowerRotate(setup_player_t *p, UINT8 num)
}
else if (M_MenuBackPressed(num))
{
p->mdepth = CSSTEP_COLORS;
p->mdepth = CSSTEP_FOLLOWERCATEGORY;
S_StartSound(NULL, sfx_s3k5b);
M_SetMenuDelay(num);
}
else if (M_MenuExtraPressed(num))
{
p->mdepth = CSSTEP_FOLLOWERCATEGORY;
p->followercategory = -1;
p->rotate = CSROTATETICS;
p->hitlag = true;
S_StartSound(NULL, sfx_s3k7b); //sfx_s3kc3s
M_SetMenuDelay(num);
}
}
static void M_HandleFollowerColorRotate(setup_player_t *p, UINT8 num)
@ -2849,10 +3003,24 @@ static void M_HandleFollowerColorRotate(setup_player_t *p, UINT8 num)
}
else if (M_MenuBackPressed(num))
{
M_GetFollowerState(p);
p->mdepth = CSSTEP_FOLLOWER;
S_StartSound(NULL, sfx_s3k5b);
M_SetMenuDelay(num);
}
else if (M_MenuExtraPressed(num))
{
if (p->followercolor == FOLLOWERCOLOR_MATCH)
p->followercolor = FOLLOWERCOLOR_OPPOSITE;
else if (p->followercolor == followers[p->followern].defaultcolor)
p->followercolor = FOLLOWERCOLOR_MATCH;
else
p->followercolor = followers[p->followern].defaultcolor;
p->rotate = CSROTATETICS;
p->hitlag = true;
S_StartSound(NULL, sfx_s3k7b); //sfx_s3kc3s
M_SetMenuDelay(num);
}
}
boolean M_CharacterSelectHandler(INT32 choice)
@ -2901,6 +3069,9 @@ boolean M_CharacterSelectHandler(INT32 choice)
case CSSTEP_COLORS: // Select color
M_HandleColorRotate(p, i);
break;
case CSSTEP_FOLLOWERCATEGORY:
M_HandleFollowerCategoryRotate(p, i);
break;
case CSSTEP_FOLLOWER:
M_HandleFollowerRotate(p, i);
break;
@ -2956,33 +3127,27 @@ static void M_MPConfirmCharacterSelection(void)
UINT8 i;
INT16 col;
char commandnames[][MAXSTRINGLENGTH] = { "skin ", "skin2 ", "skin3 ", "skin4 "};
// ^ laziness 100 (we append a space directly so that we don't have to do it later too!!!!)
for (i = 0; i < splitscreen +1; i++)
{
char cmd[MAXSTRINGLENGTH];
// colour
// (convert the number that's saved to a string we can use)
col = setup_player[i].color;
CV_StealthSetValue(&cv_playercolor[i], col);
// follower
CV_StealthSetValue(&cv_follower[i], setup_player[i].followern);
// follower color
CV_StealthSetValue(&cv_followercolor[i], setup_player[i].followercolor);
if (setup_player[i].followern < 0)
CV_StealthSet(&cv_follower[i], "None");
else
CV_StealthSet(&cv_follower[i], followers[setup_player[i].followern].name);
// finally, call the skin[x] console command.
// This will call SendNameAndColor which will synch everything we sent here and apply the changes!
// This is a hack to make sure we call Skin[x]_OnChange afterwards
CV_StealthSetValue(&cv_skin[i], -1);
CV_StealthSet(&cv_skin[i], skins[setup_player[i].skin].name);
strcpy(cmd, commandnames[i]);
strcat(cmd, skins[setup_player[i].skin].name);
COM_ImmedExecute(cmd);
// ...actually, let's do this last - Skin_OnChange has some return-early occasions
// follower color
CV_SetValue(&cv_followercolor[i], setup_player[i].followercolor);
}
M_ClearMenus(true);
@ -3004,6 +3169,8 @@ void M_CharacterSelectTick(void)
setup_player[i].rotate--;
else if (setup_player[i].rotate < 0)
setup_player[i].rotate++;
else
setup_player[i].hitlag = false;
if (i >= setup_numplayers)
continue;
@ -3034,7 +3201,7 @@ void M_CharacterSelectTick(void)
optionsmenu.profile->color = setup_player[0].color;
// save follower
strcpy(optionsmenu.profile->follower, followers[setup_player[0].followern].skinname);
strcpy(optionsmenu.profile->follower, followers[setup_player[0].followern].name);
optionsmenu.profile->followercolor = setup_player[0].followercolor;
// reset setup_player
@ -3051,7 +3218,10 @@ void M_CharacterSelectTick(void)
CV_StealthSet(&cv_skin[i], skins[setup_player[i].skin].name);
CV_StealthSetValue(&cv_playercolor[i], setup_player[i].color);
CV_StealthSetValue(&cv_follower[i], setup_player[i].followern);
if (setup_player[i].followern < 0)
CV_StealthSet(&cv_follower[i], "None");
else
CV_StealthSet(&cv_follower[i], followers[setup_player[i].followern].name);
CV_StealthSetValue(&cv_followercolor[i], setup_player[i].followercolor);
}
@ -3269,7 +3439,15 @@ static void M_LevelListFromGametype(INT16 gt)
while (cup)
{
if (cup->unlockrequired == -1 || unlockables[cup->unlockrequired].unlocked)
{
highestid = cup->id;
if (Playing() && mapheaderinfo[gamemap-1] && mapheaderinfo[gamemap-1]->cup == cup)
{
cupgrid.x = cup->id % CUPMENU_COLUMNS;
cupgrid.y = (cup->id / CUPMENU_COLUMNS) % CUPMENU_ROWS;
cupgrid.pageno = cup->id / (CUPMENU_COLUMNS * CUPMENU_ROWS);
}
}
cup = cup->next;
}
@ -4775,7 +4953,7 @@ void M_HandleProfileSelect(INT32 ch)
#if 0
if (optionsmenu.profilen == 0)
{
M_StartMessage(M_GetText("Are you sure you wish\nto use the Guest Profile?\nThis profile cannot be customised.\nIt is recommended to create\na new Profile instead.\n\n(Press A to confirm)"), FUNCPTRCAST(M_FirstPickProfile), MM_YESNO);
M_StartMessage(M_GetText("Are you sure you wish\nto use the Guest Profile?\nThis profile cannot be customised.\nIt is recommended to create\na new Profile instead.\n\nPress (A) to confirm or (B) to cancel"), FUNCPTRCAST(M_FirstPickProfile), MM_YESNO);
return;
}
#endif
@ -4917,7 +5095,7 @@ void M_ConfirmProfile(INT32 choice)
}
else
{
M_StartMessage(M_GetText("Are you sure you wish to\nselect this profile?\n\n(Press A to confirm)"), FUNCPTRCAST(M_FirstPickProfile), MM_YESNO);
M_StartMessage(M_GetText("Are you sure you wish to\nselect this profile?\n\nPress (A) to confirm or (B) to cancel"), FUNCPTRCAST(M_FirstPickProfile), MM_YESNO);
M_SetMenuDelay(pid);
}
}
@ -5123,7 +5301,7 @@ void M_ProfileControlsConfirm(INT32 choice)
{
(void)choice;
//M_StartMessage(M_GetText("Exiting will save the control changes\nfor this Profile.\nIs this okay?\n\n(Press A to confirm)"), FUNCPTRCAST(M_ProfileControlSaveResponse), MM_YESNO);
//M_StartMessage(M_GetText("Exiting will save the control changes\nfor this Profile.\nIs this okay?\n\nPress (A) to confirm or (B) to cancel"), FUNCPTRCAST(M_ProfileControlSaveResponse), MM_YESNO);
// TODO: Add a graphic for controls saving, instead of obnoxious prompt.
M_ProfileControlSaveResponse(MA_YES);
@ -5500,7 +5678,7 @@ void M_CheckProfileData(INT32 choice)
if (np < 2)
{
S_StartSound(NULL, sfx_s3k7b);
M_StartMessage("There are no custom profiles.\n\n(Press any button)", NULL, MM_NOTHING);
M_StartMessage("There are no custom profiles.\n\nPress (B)", NULL, MM_NOTHING);
return;
}
@ -5564,9 +5742,9 @@ void M_HandleProfileErase(INT32 choice)
else if (M_MenuConfirmPressed(pid))
{
if (optionsmenu.eraseprofilen == cv_currprofile.value)
M_StartMessage("Your ""\x85""current profile""\x80"" will be erased.\nAre you sure you want to proceed?\nDeleting this profile will also\nreturn you to the title screen.\n\n(Press A to confirm)", FUNCPTRCAST(M_EraseProfileResponse), MM_YESNO);
M_StartMessage("Your ""\x85""current profile""\x80"" will be erased.\nAre you sure you want to proceed?\nDeleting this profile will also\nreturn you to the title screen.\n\nPress (A) to confirm or (B) to cancel", FUNCPTRCAST(M_EraseProfileResponse), MM_YESNO);
else
M_StartMessage("This profile will be erased.\nAre you sure you want to proceed?\n\n(Press A to confirm)", FUNCPTRCAST(M_EraseProfileResponse), MM_YESNO);
M_StartMessage("This profile will be erased.\nAre you sure you want to proceed?\n\nPress (A) to confirm or (B) to cancel", FUNCPTRCAST(M_EraseProfileResponse), MM_YESNO);
M_SetMenuDelay(pid);
}
@ -5700,6 +5878,8 @@ struct pausemenu_s pausemenu;
// Pause menu!
void M_OpenPauseMenu(void)
{
INT32 i = 0;
currentMenu = &PAUSE_MainDef;
// Ready the variables
@ -5716,6 +5896,8 @@ void M_OpenPauseMenu(void)
PAUSE_Main[mpause_addons].status = IT_DISABLED;
PAUSE_Main[mpause_switchmap].status = IT_DISABLED;
PAUSE_Main[mpause_restartmap].status = IT_DISABLED;
PAUSE_Main[mpause_tryagain].status = IT_DISABLED;
#ifdef HAVE_DISCORDRPC
PAUSE_Main[mpause_discordrequests].status = IT_DISABLED;
#endif
@ -5735,9 +5917,36 @@ void M_OpenPauseMenu(void)
if (server || IsPlayerAdmin(consoleplayer))
{
PAUSE_Main[mpause_switchmap].status = IT_STRING | IT_SUBMENU;
for (i = 0; i < PAUSE_GamemodesDef.numitems; i++)
{
if (PAUSE_GamemodesMenu[i].mvar2 != gametype)
continue;
PAUSE_GamemodesDef.lastOn = i;
break;
}
PAUSE_Main[mpause_restartmap].status = IT_STRING | IT_CALL;
PAUSE_Main[mpause_addons].status = IT_STRING | IT_CALL;
}
}
else if (!netgame && !demo.playback)
{
boolean retryallowed = (modeattacking != ATTACKING_NONE);
if (G_GametypeUsesLives())
{
for (i = 0; i <= splitscreen; i++)
{
if (players[g_localplayers[i]].lives <= 1)
continue;
retryallowed = true;
break;
}
}
if (retryallowed)
{
PAUSE_Main[mpause_tryagain].status = IT_STRING | IT_CALL;
}
}
if (G_GametypeHasSpectators())
{
@ -5766,6 +5975,7 @@ void M_QuitPauseMenu(INT32 choice)
void M_PauseTick(void)
{
pausemenu.offset /= 2;
pausemenu.ticker++;
if (pausemenu.closing)
{
@ -5814,6 +6024,37 @@ boolean M_PauseInputs(INT32 ch)
return false;
}
// Restart map
void M_RestartMap(INT32 choice)
{
(void)choice;
M_ClearMenus(false);
COM_ImmedExecute("restartlevel");
}
// Try again
void M_TryAgain(INT32 choice)
{
(void)choice;
if (demo.playback)
return;
if (netgame || !Playing()) // Should never happen!
return;
M_ClearMenus(false);
if (modeattacking != ATTACKING_NONE)
{
G_CheckDemoStatus(); // Cancel recording
M_StartTimeAttack(-1);
}
else
{
G_SetRetryFlag();
}
}
// Pause spectate / join functions
void M_ConfirmSpectate(INT32 choice)
{
@ -5828,7 +6069,7 @@ void M_ConfirmEnterGame(INT32 choice)
(void)choice;
if (!cv_allowteamchange.value)
{
M_StartMessage(M_GetText("The server is not allowing\nteam changes at this time.\nPress a key.\n"), NULL, MM_NOTHING);
M_StartMessage(M_GetText("The server is not allowing\nteam changes at this time.\n\nPress (B)\n"), NULL, MM_NOTHING);
return;
}
M_QuitPauseMenu(-1);
@ -5860,7 +6101,7 @@ void M_EndGame(INT32 choice)
if (!Playing())
return;
M_StartMessage(M_GetText("Are you sure you want to return\nto the title screen?\n(Press A to confirm)\n"), FUNCPTRCAST(M_ExitGameResponse), MM_YESNO);
M_StartMessage(M_GetText("Are you sure you want to return\nto the title screen?\nPress (A) to confirm or (B) to cancel\n"), FUNCPTRCAST(M_ExitGameResponse), MM_YESNO);
}
@ -6042,7 +6283,7 @@ void M_ReplayHut(INT32 choice)
}
if (!preparefilemenu(false, true))
{
M_StartMessage("No replays found.\n\n(Press a key)\n", NULL, MM_NOTHING);
M_StartMessage("No replays found.\n\nPress (B)\n", NULL, MM_NOTHING);
return;
}
else if (!demo.inreplayhut)
@ -6118,7 +6359,7 @@ void M_HandleReplayHutList(INT32 choice)
if (!preparefilemenu(false, true))
{
S_StartSound(NULL, sfx_s224);
M_StartMessage(va("%c%s\x80\nThis folder is empty.\n\n(Press a key)\n", ('\x80' + (highlightflags>>V_CHARCOLORSHIFT)), M_AddonsHeaderPath()),NULL,MM_NOTHING);
M_StartMessage(va("%c%s\x80\nThis folder is empty.\n\nPress (B)\n", ('\x80' + (highlightflags>>V_CHARCOLORSHIFT)), M_AddonsHeaderPath()),NULL,MM_NOTHING);
menupath[menupathindex[++menudepthleft]] = 0;
if (!preparefilemenu(true, true))
@ -6137,7 +6378,7 @@ void M_HandleReplayHutList(INT32 choice)
else
{
S_StartSound(NULL, sfx_s26d);
M_StartMessage(va("%c%s\x80\nThis folder is too deep to navigate to!\n\n(Press a key)\n", ('\x80' + (highlightflags>>V_CHARCOLORSHIFT)), M_AddonsHeaderPath()),NULL,MM_NOTHING);
M_StartMessage(va("%c%s\x80\nThis folder is too deep to navigate to!\n\nPress (B)\n", ('\x80' + (highlightflags>>V_CHARCOLORSHIFT)), M_AddonsHeaderPath()),NULL,MM_NOTHING);
menupath[menupathindex[menudepthleft]] = 0;
}
break;
@ -6264,7 +6505,7 @@ void M_Addons(INT32 choice)
if (!preparefilemenu(false, false))
{
M_StartMessage(va("No files/folders found.\n\n%s\n\n(Press a key)\n", LOCATIONSTRING1),NULL,MM_NOTHING);
M_StartMessage(va("No files/folders found.\n\n%s\n\nPress (B)\n", LOCATIONSTRING1),NULL,MM_NOTHING);
return;
}
else
@ -6297,7 +6538,7 @@ char *M_AddonsHeaderPath(void)
#define UNEXIST S_StartSound(NULL, sfx_s26d);\
M_SetupNextMenu(MISC_AddonsDef.prevMenu, false);\
M_StartMessage(va("\x82%s\x80\nThis folder no longer exists!\nAborting to main menu.\n\n(Press a key)\n", M_AddonsHeaderPath()),NULL,MM_NOTHING)
M_StartMessage(va("\x82%s\x80\nThis folder no longer exists!\nAborting to main menu.\n\nPress (B)\n", M_AddonsHeaderPath()),NULL,MM_NOTHING)
#define CLEARNAME Z_Free(refreshdirname);\
refreshdirname = NULL
@ -6341,19 +6582,19 @@ void M_AddonsRefresh(void)
{
S_StartSound(NULL, sfx_s26d);
if (refreshdirmenu & REFRESHDIR_MAX)
message = va("%c%s\x80\nMaximum number of addons reached.\nA file could not be loaded.\nIf you wish to play with this addon, restart the game to clear existing ones.\n\n(Press a key)\n", ('\x80' + (highlightflags>>V_CHARCOLORSHIFT)), refreshdirname);
message = va("%c%s\x80\nMaximum number of addons reached.\nA file could not be loaded.\nIf you wish to play with this addon, restart the game to clear existing ones.\n\nPress (B)\n", ('\x80' + (highlightflags>>V_CHARCOLORSHIFT)), refreshdirname);
else
message = va("%c%s\x80\nA file was not loaded.\nCheck the console log for more info.\n\n(Press a key)\n", ('\x80' + (highlightflags>>V_CHARCOLORSHIFT)), refreshdirname);
message = va("%c%s\x80\nA file was not loaded.\nCheck the console log for more info.\n\nPress (B)\n", ('\x80' + (highlightflags>>V_CHARCOLORSHIFT)), refreshdirname);
}
else if (refreshdirmenu & (REFRESHDIR_WARNING|REFRESHDIR_ERROR))
{
S_StartSound(NULL, sfx_s224);
message = va("%c%s\x80\nA file was loaded with %s.\nCheck the console log for more info.\n\n(Press a key)\n", ('\x80' + (highlightflags>>V_CHARCOLORSHIFT)), refreshdirname, ((refreshdirmenu & REFRESHDIR_ERROR) ? "errors" : "warnings"));
message = va("%c%s\x80\nA file was loaded with %s.\nCheck the console log for more info.\n\nPress (B)\n", ('\x80' + (highlightflags>>V_CHARCOLORSHIFT)), refreshdirname, ((refreshdirmenu & REFRESHDIR_ERROR) ? "errors" : "warnings"));
}
else if (majormods && !prevmajormods)
{
S_StartSound(NULL, sfx_s221);
message = va("%c%s\x80\nYou've loaded a gameplay-modifying addon.\n\nRecord Attack has been disabled, but you\ncan still play alone in local Multiplayer.\n\nIf you wish to play Record Attack mode, restart the game to disable loaded addons.\n\n(Press a key)\n", ('\x80' + (highlightflags>>V_CHARCOLORSHIFT)), refreshdirname);
message = va("%c%s\x80\nYou've loaded a gameplay-modifying addon.\n\nRecord Attack has been disabled, but you\ncan still play alone in local Multiplayer.\n\nIf you wish to play Record Attack mode, restart the game to disable loaded addons.\n\nPress (B)\n", ('\x80' + (highlightflags>>V_CHARCOLORSHIFT)), refreshdirname);
prevmajormods = majormods;
}
@ -6472,7 +6713,7 @@ void M_HandleAddons(INT32 choice)
if (!preparefilemenu(false, false))
{
S_StartSound(NULL, sfx_s224);
M_StartMessage(va("%c%s\x80\nThis folder is empty.\n\n(Press a key)\n", ('\x80' + (highlightflags>>V_CHARCOLORSHIFT)), M_AddonsHeaderPath()),NULL,MM_NOTHING);
M_StartMessage(va("%c%s\x80\nThis folder is empty.\n\nPress (B)\n", ('\x80' + (highlightflags>>V_CHARCOLORSHIFT)), M_AddonsHeaderPath()),NULL,MM_NOTHING);
menupath[menupathindex[++menudepthleft]] = 0;
if (!preparefilemenu(true, false))
@ -6491,7 +6732,7 @@ void M_HandleAddons(INT32 choice)
else
{
S_StartSound(NULL, sfx_s26d);
M_StartMessage(va("%c%s\x80\nThis folder is too deep to navigate to!\n\n(Press a key)\n", ('\x80' + (highlightflags>>V_CHARCOLORSHIFT)), M_AddonsHeaderPath()),NULL,MM_NOTHING);
M_StartMessage(va("%c%s\x80\nThis folder is too deep to navigate to!\n\nPress (B)\n", ('\x80' + (highlightflags>>V_CHARCOLORSHIFT)), M_AddonsHeaderPath()),NULL,MM_NOTHING);
menupath[menupathindex[menudepthleft]] = 0;
}
break;
@ -6507,11 +6748,11 @@ void M_HandleAddons(INT32 choice)
break;
case EXT_TXT:
M_StartMessage(va("%c%s\x80\nThis file may not be a console script.\nAttempt to run anyways? \n\n(Press A to confirm)\n", ('\x80' + (highlightflags>>V_CHARCOLORSHIFT)), dirmenu[dir_on[menudepthleft]]+DIR_STRING),FUNCPTRCAST(M_AddonExec),MM_YESNO);
M_StartMessage(va("%c%s\x80\nThis file may not be a console script.\nAttempt to run anyways? \n\nPress (A) to confirm or (B) to cancel\n\n", ('\x80' + (highlightflags>>V_CHARCOLORSHIFT)), dirmenu[dir_on[menudepthleft]]+DIR_STRING),FUNCPTRCAST(M_AddonExec),MM_YESNO);
break;
case EXT_CFG:
M_StartMessage(va("%c%s\x80\nThis file may modify your settings.\nAttempt to run anyways? \n\n(Press A to confirm)\n", ('\x80' + (highlightflags>>V_CHARCOLORSHIFT)), dirmenu[dir_on[menudepthleft]]+DIR_STRING),FUNCPTRCAST(M_AddonExec),MM_YESNO);
M_StartMessage(va("%c%s\x80\nThis file may modify your settings.\nAttempt to run anyways? \n\nPress (A) to confirm or (B) to cancel\n", ('\x80' + (highlightflags>>V_CHARCOLORSHIFT)), dirmenu[dir_on[menudepthleft]]+DIR_STRING),FUNCPTRCAST(M_AddonExec),MM_YESNO);
break;
case EXT_LUA:

View file

@ -402,10 +402,7 @@ static int lib_pRandomByte(lua_State *L)
static int lib_pRandomKey(lua_State *L)
{
INT32 a = (INT32)luaL_checkinteger(L, 1);
NOHUD
if (a > 65536)
LUA_UsageWarning(L, "P_RandomKey: range > 65536 is undefined behavior");
lua_pushinteger(L, P_RandomKey(PR_UNDEFINED, a));
demo_writerng = 2;
return 1;
@ -415,15 +412,13 @@ static int lib_pRandomRange(lua_State *L)
{
INT32 a = (INT32)luaL_checkinteger(L, 1);
INT32 b = (INT32)luaL_checkinteger(L, 2);
NOHUD
if (b < a) {
if (b < a)
{
INT32 c = a;
a = b;
b = c;
}
if ((b-a+1) > 65536)
LUA_UsageWarning(L, "P_RandomRange: range > 65536 is undefined behavior");
lua_pushinteger(L, P_RandomRange(PR_UNDEFINED, a, b));
demo_writerng = 2;
return 1;

View file

@ -25,20 +25,56 @@
// RNG functions (not synched)
// ---------------------------
ATTRINLINE static UINT32 FUNCINLINE __external_prng__(void)
{
UINT32 rnd = rand();
#if RAND_MAX < 65535
// Compensate for especially bad randomness.
UINT32 rndv = (rand() & 1) << 15;
rnd ^= rndv;
#endif
// Shuffle like we do for our own PRNG, since RAND_MAX
// tends to be [0, INT32_MAX] instead of [0, UINT32_MAX].
rnd ^= rnd >> 13;
rnd ^= rnd >> 11;
rnd ^= rnd << 21;
return (rnd * 36548569);
}
ATTRINLINE static UINT32 FUNCINLINE __external_prng_bound__(UINT32 bound)
{
// Do rejection sampling to remove the modulo bias.
UINT32 threshold = -bound % bound;
for (;;)
{
UINT32 r = __external_prng__();
if (r >= threshold)
{
return r % bound;
}
}
}
/** Provides a random 32-bit number. Distribution is uniform.
* As with all M_Random functions, not synched in netgames.
*
* \return A random 32-bit number.
*/
UINT32 M_Random(void)
{
return __external_prng__();
}
/** Provides a random fixed point number. Distribution is uniform.
* As with all M_Random functions, not synched in netgames.
*
* \return A random fixed point number from [0,1).
* \return A random fixed point number from [0,1].
*/
fixed_t M_RandomFixed(void)
{
#if RAND_MAX < 65535
// Compensate for insufficient randomness.
fixed_t rndv = (rand()&1)<<15;
return rand()^rndv;
#else
return (rand() & 0xFFFF);
#endif
return (fixed_t)(__external_prng_bound__(FRACUNIT));
}
/** Provides a random byte. Distribution is uniform.
@ -48,7 +84,7 @@ fixed_t M_RandomFixed(void)
*/
UINT8 M_RandomByte(void)
{
return (rand() & 0xFF);
return (UINT8)(__external_prng_bound__(UINT8_MAX));
}
/** Provides a random integer for picking random elements from an array.
@ -58,9 +94,9 @@ UINT8 M_RandomByte(void)
* \param a Number of items in array.
* \return A random integer from [0,a).
*/
INT32 M_RandomKey(INT32 a)
UINT32 M_RandomKey(UINT32 a)
{
return (INT32)((rand()/((unsigned)RAND_MAX+1.0f))*a);
return __external_prng_bound__(a);
}
/** Provides a random integer in a given range.
@ -73,7 +109,7 @@ INT32 M_RandomKey(INT32 a)
*/
INT32 M_RandomRange(INT32 a, INT32 b)
{
return (INT32)((rand()/((unsigned)RAND_MAX+1.0f))*(b-a+1))+a;
return (INT32)(__external_prng_bound__((b - a) + 1)) + a;
}
@ -92,23 +128,56 @@ typedef struct
static rng_t rng; // The entire PRNG state
/** Provides a random fixed point number.
/** Provides a random 32 bit integer.
* This is a variant of an xorshift PRNG; state fits in a 32 bit integer structure.
*
* \return A random fixed point number from [0,1).
* \return A random, uniformly distributed number from [0,UINT32_MAX].
*/
ATTRINLINE static fixed_t FUNCINLINE __internal_prng__(pr_class_t pr_class)
ATTRINLINE static UINT32 FUNCINLINE __internal_prng__(pr_class_t pr_class)
{
rng.seed[pr_class] ^= rng.seed[pr_class] >> 13;
rng.seed[pr_class] ^= rng.seed[pr_class] >> 11;
rng.seed[pr_class] ^= rng.seed[pr_class] << 21;
return ( (rng.seed[pr_class] * 36548569) >> 4) & (FRACUNIT-1);
return (rng.seed[pr_class] * 36548569);
}
/** Provides a random number within a specified range.
*
* \return A random, uniformly distributed number from [0,bound].
*/
ATTRINLINE static UINT32 FUNCINLINE __internal_prng_bound__(pr_class_t pr_class, UINT32 bound)
{
// Do rejection sampling to remove the modulo bias.
UINT32 threshold = -bound % bound;
for (;;)
{
UINT32 r = __internal_prng__(pr_class);
if (r >= threshold)
{
return r % bound;
}
}
}
/** Provides a random fixed point number. Distribution is uniform.
* Literally a wrapper for the internal PRNG function.
*
* \return A random fixed point number from [0,1).
* \return A random fixed point number from [0,UINT32_MAX].
*/
#ifndef DEBUGRANDOM
UINT32 P_Random(pr_class_t pr_class)
{
#else
UINT32 P_RandomD(const char *rfile, INT32 rline, pr_class_t pr_class)
{
CONS_Printf("P_Random(%u) at: %sp %d\n", pr_class, rfile, rline);
#endif
return __internal_prng__(pr_class);
}
/** Provides a random fixed point number. Distribution is uniform.
*
* \return A random fixed point number from [0,1].
*/
#ifndef DEBUGRANDOM
fixed_t P_RandomFixed(pr_class_t pr_class)
@ -118,14 +187,14 @@ fixed_t P_RandomFixedD(const char *rfile, INT32 rline, pr_class_t pr_class)
{
CONS_Printf("P_RandomFixed(%u) at: %sp %d\n", pr_class, rfile, rline);
#endif
return __internal_prng__(pr_class);
return (fixed_t)(__internal_prng_bound__(pr_class, FRACUNIT));
}
/** Provides a random byte. Distribution is uniform.
* If you're curious, (&0xFF00) >> 8 gives the same result
* as a fixed point multiplication by 256.
*
* \return Random integer from [0, 255].
* \return Random integer from [0,255].
* \sa __internal_prng__
*/
#ifndef DEBUGRANDOM
@ -136,7 +205,7 @@ UINT8 P_RandomByteD(const char *rfile, INT32 rline, pr_class_t pr_class)
{
CONS_Printf("P_RandomByte(%u) at: %sp %d\n", pr_class, rfile, rline);
#endif
return (UINT8)((__internal_prng__(pr_class) & 0xFF00) >> 8);
return (UINT8)(__internal_prng_bound__(pr_class, UINT8_MAX));
}
/** Provides a random integer for picking random elements from an array.
@ -144,23 +213,22 @@ UINT8 P_RandomByteD(const char *rfile, INT32 rline, pr_class_t pr_class)
* NOTE: Maximum range is 65536.
*
* \param a Number of items in array.
* \return A random integer from [0,a).
* \return A random integer from [0,a].
* \sa __internal_prng__
*/
#ifndef DEBUGRANDOM
INT32 P_RandomKey(pr_class_t pr_class, INT32 a)
UINT32 P_RandomKey(pr_class_t pr_class, UINT32 a)
{
#else
INT32 P_RandomKeyD(const char *rfile, INT32 rline, pr_class_t pr_class, INT32 a)
UINT32 P_RandomKeyD(const char *rfile, INT32 rline, pr_class_t pr_class, UINT32 a)
{
CONS_Printf("P_RandomKey(%u) at: %sp %d\n", pr_class, rfile, rline);
#endif
return (INT32)(((INT64)__internal_prng__(pr_class) * a) >> FRACBITS);
return __internal_prng_bound__(pr_class, a);
}
/** Provides a random integer in a given range.
* Distribution is uniform.
* NOTE: Maximum range is 65536.
*
* \param a Lower bound.
* \param b Upper bound.
@ -175,7 +243,7 @@ INT32 P_RandomRangeD(const char *rfile, INT32 rline, pr_class_t pr_class, INT32
{
CONS_Printf("P_RandomRange(%u) at: %sp %d\n", pr_class, rfile, rline);
#endif
return (INT32)(((INT64)__internal_prng__(pr_class) * (b - a + 1)) >> FRACBITS) + a;
return (INT32)(__internal_prng_bound__(pr_class, (b - a) + 1)) + a;
}
@ -187,13 +255,13 @@ INT32 P_RandomRangeD(const char *rfile, INT32 rline, pr_class_t pr_class, INT32
/** Peeks to see what the next result from the PRNG will be.
* Used for debugging.
*
* \return A 'random' fixed point number from [0,1).
* \return A 'random' number from [0,UINT32_MAX]
* \sa __internal_prng__
*/
fixed_t P_RandomPeek(pr_class_t pr_class)
UINT32 P_RandomPeek(pr_class_t pr_class)
{
UINT32 r = rng.seed[pr_class];
fixed_t ret = __internal_prng__(pr_class);
UINT32 ret = __internal_prng__(pr_class);
rng.seed[pr_class] = r;
return ret;
}

View file

@ -69,37 +69,41 @@ typedef enum
// P_Random functions pulls random bytes from a PRNG that is network synced.
// RNG functions
UINT32 M_Random(void);
fixed_t M_RandomFixed(void);
UINT8 M_RandomByte(void);
INT32 M_RandomKey(INT32 a);
UINT32 M_RandomKey(UINT32 a);
INT32 M_RandomRange(INT32 a, INT32 b);
// PRNG functions
#ifdef DEBUGRANDOM
#define P_Random(c) P_RandomD(__FILE__, __LINE__, c)
#define P_RandomFixed(c) P_RandomFixedD(__FILE__, __LINE__, c)
#define P_RandomByte(c) P_RandomByteD(__FILE__, __LINE__, c)
#define P_RandomKey(c, d) P_RandomKeyD(__FILE__, __LINE__, c, d)
#define P_RandomRange(c, d, e) P_RandomRangeD(__FILE__, __LINE__, c, d, e)
UINT32 P_RandomD(const char *rfile, INT32 rline, pr_class_t pr_class);
fixed_t P_RandomFixedD(const char *rfile, INT32 rline, pr_class_t pr_class);
UINT8 P_RandomByteD(const char *rfile, INT32 rline, pr_class_t pr_class);
INT32 P_RandomKeyD(const char *rfile, INT32 rline, pr_class_t pr_class, INT32 a);
UINT32 P_RandomKeyD(const char *rfile, INT32 rline, pr_class_t pr_class, UINT32 a);
INT32 P_RandomRangeD(const char *rfile, INT32 rline, pr_class_t pr_class, INT32 a, INT32 b);
#else
UINT32 P_Random(pr_class_t pr_class);
fixed_t P_RandomFixed(pr_class_t pr_class);
UINT8 P_RandomByte(pr_class_t pr_class);
INT32 P_RandomKey(pr_class_t pr_class, INT32 a);
UINT32 P_RandomKey(pr_class_t pr_class, UINT32 a);
INT32 P_RandomRange(pr_class_t pr_class, INT32 a, INT32 b);
#endif
// Macros for other functions
#define M_SignedRandom() ((INT32)M_RandomByte() - 128) // [-128, 127] signed byte, originally a
#define P_SignedRandom(pr) ((INT32)P_RandomByte(pr) - 128) // function of its own, moved to a macro
#define M_SignedRandom() ((INT32)M_RandomByte() + INT8_MIN) // [-128, 127] signed byte, originally a
#define P_SignedRandom(pr) ((INT32)P_RandomByte(pr) + INT8_MIN) // function of its own, moved to a macro
#define M_RandomChance(p) (M_RandomFixed() < p) // given fixed point probability, p, between 0 (0%)
#define P_RandomChance(pr, p) (P_RandomFixed(pr) < p) // and FRACUNIT (100%), returns true p% of the time
// Debugging
fixed_t P_RandomPeek(pr_class_t pr_class);
UINT32 P_RandomPeek(pr_class_t pr_class);
// Working with the seed for PRNG
#ifdef DEBUGRANDOM

View file

@ -217,7 +217,7 @@ boolean Obj_OrbinautJawzCollide(mobj_t *t1, mobj_t *t2)
S_StartSound(t2, t2->info->deathsound);
P_KillMobj(t2, t1, t1, DMG_NORMAL);
P_SetObjectMomZ(t2, 8*FRACUNIT, false);
P_SetObjectMomZ(t2, 24*FRACUNIT, false);
P_InstaThrust(t2, bounceangle, 16*FRACUNIT);
P_SpawnMobj(t2->x/2 + t1->x/2, t2->y/2 + t1->y/2, t2->z/2 + t1->z/2, MT_ITEMCLASH);
@ -254,7 +254,7 @@ boolean Obj_OrbinautJawzCollide(mobj_t *t1, mobj_t *t2)
S_StartSound(t1, t1->info->deathsound);
P_KillMobj(t1, t2, t2, DMG_NORMAL);
P_SetObjectMomZ(t1, 8*FRACUNIT, false);
P_SetObjectMomZ(t1, 24*FRACUNIT, false);
P_InstaThrust(t1, bounceangle, 16*FRACUNIT);
}

View file

@ -1585,6 +1585,32 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget
target->fuse = 1;
break;
case MT_BANANA:
case MT_BANANA_SHIELD:
{
const UINT8 numParticles = 8;
const angle_t diff = ANGLE_MAX / numParticles;
UINT8 i;
for (i = 0; i < numParticles; i++)
{
mobj_t *spark = P_SpawnMobjFromMobj(target, 0, 0, 0, MT_BANANA_SPARK);
spark->angle = (diff * i) - (diff / 2);
if (inflictor != NULL && P_MobjWasRemoved(inflictor) == false)
{
spark->angle += K_MomentumAngle(inflictor);
spark->momx += inflictor->momx / 2;
spark->momy += inflictor->momy / 2;
spark->momz += inflictor->momz / 2;
}
P_SetObjectMomZ(spark, (12 + P_RandomRange(PR_DECORATION, -4, 4)) * FRACUNIT, true);
P_Thrust(spark, spark->angle, (12 + P_RandomRange(PR_DECORATION, -4, 4)) * spark->scale);
}
break;
}
default:
break;
}
@ -2256,6 +2282,9 @@ static void P_FlingBurst
mo->fuse = objFuse;
P_SetTarget(&mo->target, player->mo);
// We want everything from P_SpawnMobjFromMobj except scale.
objScale = FixedMul(objScale, FixedDiv(mapobjectscale, player->mo->scale));
if (objScale != FRACUNIT)
{
P_SetScale(mo, FixedMul(objScale, mo->scale));

View file

@ -2618,7 +2618,7 @@ increment_move
if (!(thing->flags & MF_NOCLIP))
{
//All things are affected by their scale.
fixed_t maxstep = P_GetThingStepUp(thing, x, y);
fixed_t maxstep = P_GetThingStepUp(thing, tryx, tryy);
if (tmceilingz - tmfloorz < thing->height)
{

View file

@ -1158,66 +1158,68 @@ fixed_t P_GetMobjGravity(mobj_t *mo)
gravityadd = -gravityadd;
}
}
else //Otherwise, sort through the other exceptions.
// Sort through the other exceptions.
switch (mo->type)
{
switch (mo->type)
{
case MT_FLINGRING:
case MT_FLINGCOIN:
case MT_FLINGBLUESPHERE:
case MT_FLINGNIGHTSCHIP:
case MT_BOUNCERING:
case MT_RAILRING:
case MT_INFINITYRING:
case MT_AUTOMATICRING:
case MT_EXPLOSIONRING:
case MT_SCATTERRING:
case MT_GRENADERING:
case MT_BOUNCEPICKUP:
case MT_RAILPICKUP:
case MT_AUTOPICKUP:
case MT_EXPLODEPICKUP:
case MT_SCATTERPICKUP:
case MT_GRENADEPICKUP:
case MT_REDFLAG:
case MT_BLUEFLAG:
if (mo->target)
case MT_FLINGRING:
case MT_FLINGCOIN:
case MT_FLINGBLUESPHERE:
case MT_FLINGNIGHTSCHIP:
case MT_BOUNCERING:
case MT_RAILRING:
case MT_INFINITYRING:
case MT_AUTOMATICRING:
case MT_EXPLOSIONRING:
case MT_SCATTERRING:
case MT_GRENADERING:
case MT_BOUNCEPICKUP:
case MT_RAILPICKUP:
case MT_AUTOPICKUP:
case MT_EXPLODEPICKUP:
case MT_SCATTERPICKUP:
case MT_GRENADEPICKUP:
case MT_REDFLAG:
case MT_BLUEFLAG:
if (mo->target)
{
// Flung items copy the gravity of their tosser.
if ((mo->target->eflags & MFE_VERTICALFLIP) && !(mo->eflags & MFE_VERTICALFLIP))
{
// Flung items copy the gravity of their tosser.
if ((mo->target->eflags & MFE_VERTICALFLIP) && !(mo->eflags & MFE_VERTICALFLIP))
{
gravityadd = -gravityadd;
mo->eflags |= MFE_VERTICALFLIP;
}
gravityadd = -gravityadd;
mo->eflags |= MFE_VERTICALFLIP;
}
break;
case MT_WATERDROP:
case MT_BATTLEBUMPER:
gravityadd /= 2;
break;
case MT_BANANA:
case MT_EGGMANITEM:
case MT_SSMINE:
case MT_LANDMINE:
case MT_DROPTARGET:
case MT_SINK:
case MT_EMERALD:
}
break;
case MT_WATERDROP:
case MT_BATTLEBUMPER:
gravityadd /= 2;
break;
case MT_BANANA:
case MT_EGGMANITEM:
case MT_SSMINE:
case MT_LANDMINE:
case MT_DROPTARGET:
case MT_SINK:
case MT_EMERALD:
if (mo->health > 0)
{
if (mo->extravalue2 > 0)
{
gravityadd *= mo->extravalue2;
}
gravityadd = (5*gravityadd)/2;
break;
case MT_KARMAFIREWORK:
gravityadd /= 3;
break;
case MT_ITEM_DEBRIS:
gravityadd *= 6;
break;
default:
break;
}
}
break;
case MT_KARMAFIREWORK:
gravityadd /= 3;
break;
case MT_ITEM_DEBRIS:
gravityadd *= 6;
break;
default:
break;
}
}
@ -1261,6 +1263,11 @@ void P_CheckGravity(mobj_t *mo, boolean affect)
//
void P_SetPitchRollFromSlope(mobj_t *mo, pslope_t *slope)
{
if (!(mo->flags & MF_SLOPE))
{
return;
}
if (slope)
{
fixed_t tempz = slope->normal.z;
@ -1764,7 +1771,7 @@ void P_XYMovement(mobj_t *mo)
S_StartSound(mo, mo->info->deathsound);
P_KillMobj(mo, NULL, NULL, DMG_NORMAL);
P_SetObjectMomZ(mo, 8*FRACUNIT, false);
P_SetObjectMomZ(mo, 24*FRACUNIT, false);
P_InstaThrust(mo, R_PointToAngle2(mo->x, mo->y, mo->x + xmove, mo->y + ymove)+ANGLE_90, 16*FRACUNIT);
}
break;
@ -3119,16 +3126,14 @@ boolean P_CanRunOnWater(mobj_t *mobj, ffloor_t *rover)
fixed_t surfaceheight = INT32_MAX;
fixed_t surfDiff = INT32_MAX;
fixed_t floorheight = INT32_MAX;
fixed_t floorDiff = INT32_MAX;
fixed_t mobjbottom = INT32_MAX;
fixed_t maxStep = INT32_MAX;
boolean doifit = false;
pslope_t *waterSlope = NULL;
angle_t ourZAng = 0;
angle_t waterZAng = 0;
angle_t moveDir = 0;
fixed_t ourZAng = 0;
fixed_t waterZAng = 0;
if (rover == NULL)
{
@ -3162,20 +3167,36 @@ boolean P_CanRunOnWater(mobj_t *mobj, ffloor_t *rover)
return false;
}
if (mobj->standingslope != NULL)
moveDir = K_MomentumAngle(mobj);
if (mobj->standingslope != NULL && mobj->standingslope->zangle != 0)
{
ourZAng = mobj->standingslope->zangle;
angle_t dir = mobj->standingslope->xydirection;
angle_t workang = mobj->standingslope->zangle;
if (workang >= ANGLE_180)
{
workang = InvAngle(workang);
dir = InvAngle(dir);
}
ourZAng = P_ReturnThrustX(mobj, dir - moveDir, AngleFixed(workang));
}
waterSlope = (flip ? *rover->b_slope : *rover->t_slope);
if (waterSlope != NULL)
if (waterSlope != NULL && waterSlope->zangle != 0)
{
waterZAng = waterSlope->zangle;
angle_t dir = waterSlope->xydirection;
angle_t workang = waterSlope->zangle;
if (workang >= ANGLE_180)
{
workang = InvAngle(workang);
dir = InvAngle(dir);
}
waterZAng = P_ReturnThrustX(mobj, dir - moveDir, AngleFixed(workang));
}
if (ourZAng != waterZAng)
if (abs(ourZAng - waterZAng) > 11*FRACUNIT)
{
// The surface slopes are different.
// The surface slopes are too different.
return false;
}
@ -3193,16 +3214,20 @@ boolean P_CanRunOnWater(mobj_t *mobj, ffloor_t *rover)
maxStep = P_GetThingStepUp(mobj, mobj->x, mobj->y);
surfDiff = flip ? (surfaceheight - mobjbottom) : (mobjbottom - surfaceheight);
// We start water run IF we can step onto it!
if (surfDiff <= maxStep && surfDiff >= 0)
{
// We start water run IF we can step-down!
floorheight = flip ? P_GetSectorCeilingZAt(mobj->subsector->sector, mobj->x, mobj->y) : P_GetSectorFloorZAt(mobj->subsector->sector, mobj->x, mobj->y);
floorDiff = flip ? (floorheight - mobjbottom) : (mobjbottom - floorheight);
if (floorDiff <= maxStep && floorDiff >= 0)
if (ourZAng < 0)
{
// ... but NOT if real floor is in range.
// FIXME: Count solid FOFs in this check
return false;
fixed_t floorheight = flip ? P_GetSectorCeilingZAt(mobj->subsector->sector, mobj->x, mobj->y) : P_GetSectorFloorZAt(mobj->subsector->sector, mobj->x, mobj->y);
fixed_t floorDiff = flip ? (floorheight - mobjbottom) : (mobjbottom - floorheight);
if (floorDiff <= maxStep && floorDiff >= -maxStep)
{
// ... but NOT if going down and real floor is in range.
// FIXME: Count solid FOFs in this check
return false;
}
}
return true;
@ -6486,8 +6511,26 @@ static boolean P_MobjDeadThink(mobj_t *mobj)
P_SetObjectMomZ(mobj, -2*FRACUNIT/3, true);
}
break;
case MT_ORBINAUT:
case MT_BANANA:
{
angle_t spin = FixedMul(FixedDiv(abs(mobj->momz), 8 * mobj->scale), ANGLE_67h);
mobj->angle -= spin;
mobj->rollangle += spin;
if (P_IsObjectOnGround(mobj) && mobj->momz * P_MobjFlip(mobj) <= 0)
{
P_RemoveMobj(mobj);
return false;
}
}
break;
case MT_BANANA_SPARK:
{
angle_t spin = FixedMul(FixedDiv(abs(mobj->momz), 8 * mobj->scale), ANGLE_22h);
mobj->rollangle += spin;
}
break;
case MT_ORBINAUT:
case MT_EGGMANITEM:
case MT_LANDMINE:
//case MT_DROPTARGET:
@ -6927,16 +6970,61 @@ static boolean P_MobjRegularThink(mobj_t *mobj)
}
}
if (P_IsObjectOnGround(mobj) && mobj->health > 1)
if (P_IsObjectOnGround(mobj))
{
S_StartSound(mobj, mobj->info->activesound);
mobj->momx = mobj->momy = 0;
mobj->health = 1;
//mobj->rollangle = 0;
if (mobj->health > 1)
{
S_StartSound(mobj, mobj->info->activesound);
mobj->momx = mobj->momy = 0;
mobj->health = 1;
}
}
else
{
// tilt n tumble
angle_t spin = FixedMul(FixedDiv(abs(mobj->momz), 8 * mobj->scale), ANGLE_67h);
mobj->angle += spin;
mobj->rollangle -= spin;
}
if (mobj->threshold > 0)
mobj->threshold--;
break;
case MT_BANANA_SPARK:
{
if (leveltime & 1)
{
mobj->spritexscale = mobj->spriteyscale = FRACUNIT;
}
else
{
if ((leveltime / 2) & 1)
{
mobj->spriteyscale = 3*FRACUNIT/2;
}
else
{
mobj->spritexscale = 3*FRACUNIT/2;
}
}
if (P_IsObjectOnGround(mobj) == true && mobj->momz * P_MobjFlip(mobj) <= 0)
{
P_SetObjectMomZ(mobj, 8*FRACUNIT, false);
if (mobj->health > 0)
{
mobj->tics = 1;
mobj->destscale = 0;
mobj->spritexscale = mobj->spriteyscale = FRACUNIT;
mobj->health = 0;
}
}
break;
}
case MT_SPB:
{
Obj_SPBThink(mobj);
@ -9473,7 +9561,7 @@ void P_MobjThinker(mobj_t *mobj)
S_StartSound(mobj, mobj->info->deathsound);
P_KillMobj(mobj, NULL, NULL, DMG_NORMAL);
P_SetObjectMomZ(mobj, 8*FRACUNIT, false);
P_SetObjectMomZ(mobj, 24*FRACUNIT, false);
P_InstaThrust(mobj, R_PointToAngle2(0, 0, mobj->momx, mobj->momy) + ANGLE_90, 16*FRACUNIT);
}
@ -9700,24 +9788,6 @@ void P_PushableThinker(mobj_t *mobj)
// Quick, optimized function for scenery
void P_SceneryThinker(mobj_t *mobj)
{
if (mobj->flags & MF_BOXICON)
{
if (!(mobj->eflags & MFE_VERTICALFLIP))
{
if (mobj->z < mobj->floorz + FixedMul(mobj->info->damage, mobj->scale))
mobj->momz = FixedMul(mobj->info->speed, mobj->scale);
else
mobj->momz = 0;
}
else
{
if (mobj->z + FixedMul(mobj->info->height, mobj->scale) > mobj->ceilingz - FixedMul(mobj->info->damage, mobj->scale))
mobj->momz = -FixedMul(mobj->info->speed, mobj->scale);
else
mobj->momz = 0;
}
}
// momentum movement
if (mobj->momx || mobj->momy)
{
@ -13681,6 +13751,8 @@ mobj_t *P_SpawnMobjFromMobj(mobj_t *mobj, fixed_t xofs, fixed_t yofs, fixed_t zo
if (!newmobj)
return NULL;
newmobj->hitlag = mobj->hitlag;
newmobj->destscale = P_ScaleFromMap(mobj->destscale, newmobj->destscale);
P_SetScale(newmobj, P_ScaleFromMap(mobj->scale, newmobj->scale));

View file

@ -126,8 +126,8 @@ typedef enum
MF_NOCLIP = 1<<12,
// Allow moves to any height, no gravity. For active floaters.
MF_FLOAT = 1<<13,
// Monitor powerup icon. These rise a bit.
MF_BOXICON = 1<<14,
// Change pitch/roll when touching slopes.
MF_SLOPE = 1<<14,
// Don't hit same species, explode on block.
// Player missiles as well as fireballs of various kinds.
MF_MISSILE = 1<<15,
@ -163,7 +163,7 @@ typedef enum
MF_NOSQUISH = 1<<30,
// Disable hitlag for this object
MF_NOHITLAGFORME = (INT32)(1U<<31),
// no more free slots, next up I suppose we can get rid of shit like MF_BOXICON?
// no more free slots, gotta get rid of more crusty base SRB2 flags
} mobjflag_t;
typedef enum

View file

@ -170,6 +170,13 @@ mapthing_t *playerstarts[MAXPLAYERS];
mapthing_t *bluectfstarts[MAXPLAYERS];
mapthing_t *redctfstarts[MAXPLAYERS];
// Global state for PartialAddWadFile/MultiSetupWadFiles
// Might be replacable with parameters, but non-trivial when the functions are called on separate tics
static SINT8 partadd_stage = -1;
static boolean partadd_important = false;
UINT16 partadd_earliestfile = UINT16_MAX;
// Maintain *ZOOM TUBE* waypoints
// Renamed because SRB2Kart owns real waypoints.
mobj_t *tubewaypoints[NUMTUBEWAYPOINTSEQUENCES][TUBEWAYPOINTSEQUENCESIZE];
@ -2624,6 +2631,7 @@ static void P_ProcessLinedefsAfterSidedefs(void)
{
size_t i = numlines;
register line_t *ld = lines;
const boolean subtractTripwire = ((mapheaderinfo[gamemap - 1]->levelflags & LF_SUBTRACTNUM) == LF_SUBTRACTNUM);
for (; i--; ld++)
{
@ -2638,7 +2646,7 @@ static void P_ProcessLinedefsAfterSidedefs(void)
if (ld->tripwire)
{
ld->blendmode = AST_ADD;
ld->blendmode = (subtractTripwire ? AST_SUBTRACT : AST_ADD);
ld->alpha = 0xff;
}
@ -7745,7 +7753,7 @@ lumpnum_t wadnamelump = LUMPERROR;
INT16 wadnamemap = 0; // gamemap based
// Initialising map data (and catching replacements)...
UINT8 P_InitMapData(INT32 numexistingmapheaders)
UINT8 P_InitMapData(boolean existingmapheaders)
{
UINT8 ret = 0;
INT32 i;
@ -7759,20 +7767,9 @@ UINT8 P_InitMapData(INT32 numexistingmapheaders)
name = mapheaderinfo[i]->lumpname;
maplump = W_CheckNumForMap(name);
// Doesn't exist?
if (maplump == INT16_MAX)
{
#ifndef DEVELOP
if (!numexistingmapheaders)
{
I_Error("P_InitMapData: Base map %s has a header but no level\n", name);
}
#endif
continue;
}
// Always check for cup cache reassociations.
// (The core assumption is that cups < headers.)
if (maplump != LUMPERROR || mapheaderinfo[i]->lumpnum != LUMPERROR)
{
cupheader_t *cup = kartcupheaders;
INT32 j;
@ -7807,6 +7804,18 @@ UINT8 P_InitMapData(INT32 numexistingmapheaders)
}
}
// Doesn't exist in this set of files?
if (maplump == LUMPERROR)
{
#ifndef DEVELOP
if (!existingmapheaders)
{
I_Error("P_InitMapData: Base map %s has a header but no level\n", name);
}
#endif
continue;
}
// No change?
if (mapheaderinfo[i]->lumpnum == maplump)
continue;
@ -7816,7 +7825,7 @@ UINT8 P_InitMapData(INT32 numexistingmapheaders)
ret |= MAPRET_ADDED;
CONS_Printf("%s\n", name);
if (numexistingmapheaders && mapheaderinfo[i]->lumpnum != LUMPERROR)
if (existingmapheaders && mapheaderinfo[i]->lumpnum != LUMPERROR)
{
G_SetGameModified(multiplayer, true); // oops, double-defined - no record attack privileges for you
@ -7866,23 +7875,32 @@ UINT8 P_InitMapData(INT32 numexistingmapheaders)
return ret;
}
UINT16 p_adding_file = INT16_MAX;
//
// Add a wadfile to the active wad files,
// replace sounds, musics, patches, textures, sprites and maps
//
boolean P_AddWadFile(const char *wadfilename)
{
UINT16 wadnum;
if ((wadnum = P_PartialAddWadFile(wadfilename)) == UINT16_MAX)
return false;
P_MultiSetupWadFiles(true);
return true;
}
//
// Add a WAD file and do the per-WAD setup stages.
// Call P_MultiSetupWadFiles as soon as possible after any number of these.
//
UINT16 P_PartialAddWadFile(const char *wadfilename)
{
size_t i, j, sreplaces = 0, mreplaces = 0, digmreplaces = 0;
INT32 numexistingmapheaders = nummapheaders;
UINT16 numlumps, wadnum;
char *name;
lumpinfo_t *lumpinfo;
//boolean texturechange = false; ///\todo Useless; broken when back-frontporting PK3 changes?
UINT8 mapsadded = 0;
// Vars to help us with the position start and amount of each resource type.
// Useful for PK3s since they use folders.
// WADs use markers for some resources, but others such as sounds are checked lump-by-lump anyway.
@ -7902,10 +7920,19 @@ boolean P_AddWadFile(const char *wadfilename)
refreshdirmenu |= REFRESHDIR_NOTLOADED;
return false;
}
else
wadnum = (UINT16)(numwadfiles-1);
p_adding_file = wadnum;
wadnum = (UINT16)(numwadfiles-1);
// Init partadd.
if (wadfiles[wadnum]->important)
{
partadd_important = true;
}
if (partadd_stage != 0)
{
partadd_earliestfile = wadnum;
}
partadd_stage = 0;
switch(wadfiles[wadnum]->type)
{
@ -7939,8 +7966,6 @@ boolean P_AddWadFile(const char *wadfilename)
// R_LoadSpritsRange(wadnum, sprPos, sprNum);
// if (texNum) // Textures. TODO: R_LoadTextures() does the folder positioning once again. New function maybe?
// R_LoadTextures();
// if (mapNum) // Maps. TODO: Actually implement the map WAD loading code, lulz.
// P_LoadWadMapRange(wadnum, mapPos, mapNum);
break;
default:
lumpinfo = wadfiles[wadnum]->lumpinfo;
@ -8005,23 +8030,14 @@ boolean P_AddWadFile(const char *wadfilename)
// TEXTURES/etc. list.
R_LoadTexturesPwad(wadnum); // numtexture changes
// Reload ANIMDEFS
P_InitPicAnims();
// Reload BRIGHT
K_InitBrightmapsPwad(wadnum);
// Flush and reload HUD graphics
//ST_UnloadGraphics();
HU_LoadGraphics();
ST_LoadGraphics();
//
// look for skins
//
R_AddSkins(wadnum); // faB: wadfile index in wadfiles[]
R_PatchSkins(wadnum); // toast: PATCH PATCH
ST_ReloadSkinFaceGraphics();
//
// edit music defs
@ -8029,37 +8045,99 @@ boolean P_AddWadFile(const char *wadfilename)
S_LoadMusicDefs(wadnum);
//
// search for maps
// extra sprite/skin data
//
mapsadded = P_InitMapData(numexistingmapheaders);
if (!mapsadded)
CONS_Printf(M_GetText("No maps added\n"));
R_LoadSpriteInfoLumps(wadnum, numlumps);
#ifdef HWRENDER
HWR_ReloadModels();
#endif
// For anything that has to be done over every wadfile at once, see P_MultiSetupWadFiles.
// reload status bar (warning should have valid player!)
if (gamestate == GS_LEVEL)
ST_Start();
// Prevent savefile cheating
if (cursaveslot > 0)
cursaveslot = 0;
if ((mapsadded & MAPRET_CURRENTREPLACED) && gamestate == GS_LEVEL && (netgame || multiplayer))
{
CONS_Printf(M_GetText("Current map %d replaced by added file, ending the level to ensure consistency.\n"), gamemap);
if (server)
SendNetXCmd(XD_EXITLEVEL, NULL, 0);
}
refreshdirmenu &= ~REFRESHDIR_GAMEDATA; // Under usual circumstances we'd wait for REFRESHDIR_GAMEDATA to disappear the next frame, but it's a bit too dangerous for that...
p_adding_file = INT16_MAX;
refreshdirmenu &= ~REFRESHDIR_GAMEDATA; // Under usual circumstances we'd wait for REFRESHDIR_ flags to disappear the next frame, but this one's a bit too dangerous for that...
return true;
}
// Only exists to make sure there's no way to overwrite partadd_stage externally
// unless you really push yourself.
SINT8 P_PartialAddGetStage(void)
{
return partadd_stage;
}
//
// Set up a series of partially added WAD files.
// Setup functions that iterate over every loaded WAD go here.
// If fullsetup false, only do one stage per call.
//
boolean P_MultiSetupWadFiles(boolean fullsetup)
{
if (partadd_stage < 0)
I_Error(M_GetText("P_MultiSetupWadFiles: Post-load addon setup attempted without loading any addons first"));
if (partadd_stage == 0)
{
// Flush and reload HUD graphics
//ST_UnloadGraphics();
HU_LoadGraphics();
ST_LoadGraphics();
ST_ReloadSkinFaceGraphics();
if (!partadd_important)
partadd_stage = -1; // everything done
else if (fullsetup)
partadd_stage++;
}
if (partadd_stage == 1)
{
// Prevent savefile cheating
if (cursaveslot >= 0)
cursaveslot = 0;
// Reload ANIMATED / ANIMDEFS
P_InitPicAnims();
// reload status bar (warning should have valid player!)
if (gamestate == GS_LEVEL)
ST_Start();
#ifdef HWRENDER
HWR_ReloadModels();
#endif
if (fullsetup)
partadd_stage++;
}
if (partadd_stage == 2)
{
UINT8 mapsadded = P_InitMapData(true);
if (!mapsadded)
CONS_Printf(M_GetText("No maps added\n"));
if ((mapsadded & MAPRET_CURRENTREPLACED)
&& (gamestate == GS_LEVEL)
&& (netgame || multiplayer))
{
CONS_Printf(M_GetText("Current map %d replaced by added file, ending the level to ensure consistency.\n"), gamemap);
if (server)
SendNetXCmd(XD_EXITLEVEL, NULL, 0);
}
//if (fullsetup)
//partadd_stage++;
partadd_stage = -1;
}
I_Assert(!fullsetup || partadd_stage < 0);
if (partadd_stage < 0)
{
partadd_important = false;
partadd_earliestfile = UINT16_MAX;
return true;
}
partadd_stage++;
return false;
}

View file

@ -96,8 +96,6 @@ INT32 P_CheckLevelFlat(const char *flatname);
extern size_t nummapthings;
extern mapthing_t *mapthings;
extern UINT16 p_adding_file;
void P_SetupLevelSky(const char *skytexname, boolean global);
void P_RespawnThings(void);
boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate);
@ -108,11 +106,29 @@ boolean P_AddWadFile(const char *wadfilename);
#define MAPRET_ADDED (1)
#define MAPRET_CURRENTREPLACED (1<<1)
UINT8 P_InitMapData(INT32 numexistingmapheaders);
UINT8 P_InitMapData(boolean existingmapheaders);
extern lumpnum_t wadnamelump;
extern INT16 wadnamemap;
#define WADNAMECHECK(name) (!strncmp(name, "WADNAME", 7))
// WARNING: The following functions should be grouped as follows:
// any amount of PartialAdds followed by MultiSetups until returned true,
// as soon as possible.
UINT16 P_PartialAddWadFile(const char *wadfilename);
// Run a single stage of multisetup, or all of them if fullsetup set.
// fullsetup true: run everything
// otherwise multiple stages
// returns true if setup finished on this call, false otherwise (always true on fullsetup)
// throws I_Error if called without any partial adds started as a safeguard
boolean P_MultiSetupWadFiles(boolean fullsetup);
// Get the current setup stage.
// if negative, no PartialAdds done since last MultiSetup
// if 0, partial adds done but MultiSetup not called yet
// if positive, setup's partway done
SINT8 P_PartialAddGetStage(void);
extern UINT16 partadd_earliestfile;
boolean P_RunSOC(const char *socfilename);
void P_LoadSoundsRange(UINT16 wadnum, UINT16 first, UINT16 num);
void P_LoadMusicsRange(UINT16 wadnum, UINT16 first, UINT16 num);

View file

@ -148,7 +148,7 @@ static void GrowAnimDefs(void)
// A prototype; here instead of p_spec.h, so they're "private"
void P_ParseANIMDEFSLump(INT32 wadNum, UINT16 lumpnum);
void P_ParseAnimationDefintion(SINT8 istexture);
void P_ParseAnimationDefintion(void);
/** Sets up texture and flat animations.
*
@ -161,8 +161,6 @@ void P_ParseAnimationDefintion(SINT8 istexture);
* \author Steven McGranahan (original), Shadow Hog (had to rewrite it to handle multiple WADs), JTE (had to rewrite it to handle multiple WADs _correctly_)
*/
static boolean animdeftempflats = false; // only until ANIMDEFS flats are removed
void P_InitPicAnims(void)
{
// Init animation
@ -182,7 +180,6 @@ void P_InitPicAnims(void)
while (animdefsLumpNum != INT16_MAX)
{
animdeftempflats = ((p_adding_file == INT16_MAX) || p_adding_file == w);
P_ParseANIMDEFSLump(w, animdefsLumpNum);
animdefsLumpNum = W_CheckNumForNamePwad("ANIMDEFS", (UINT16)w, animdefsLumpNum + 1);
}
@ -204,31 +201,14 @@ void P_InitPicAnims(void)
lastanim = anims;
for (i = 0; animdefs[i].istexture != -1; i++)
{
if (animdefs[i].istexture == 1)
{
if (R_CheckTextureNumForName(animdefs[i].startname) == -1)
continue;
lastanim->picnum = R_TextureNumForName(animdefs[i].endname);
lastanim->basepic = R_TextureNumForName(animdefs[i].startname);
}
else
{
if (animdefs[i].istexture == 2)
{
CONS_Alert(CONS_WARNING, "ANIMDEFS flats are disabled; flat support in general will be removed soon! (%s, %s)\n", animdefs[i].startname, animdefs[i].endname);
}
if (animdefs[i].istexture != 1)
continue;
}
#if 0
{
if ((W_CheckNumForName(animdefs[i].startname)) == LUMPERROR)
continue;
lastanim->picnum = R_GetFlatNumForName(animdefs[i].endname);
lastanim->basepic = R_GetFlatNumForName(animdefs[i].startname);
}
#endif
if (R_CheckTextureNumForName(animdefs[i].startname) == -1)
continue;
lastanim->picnum = R_TextureNumForName(animdefs[i].endname);
lastanim->basepic = R_TextureNumForName(animdefs[i].startname);
lastanim->istexture = animdefs[i].istexture;
lastanim->numpics = lastanim->picnum - lastanim->basepic + 1;
@ -285,21 +265,20 @@ void P_ParseANIMDEFSLump(INT32 wadNum, UINT16 lumpnum)
if (stricmp(animdefsToken, "TEXTURE") == 0)
{
Z_Free(animdefsToken);
P_ParseAnimationDefintion(1);
P_ParseAnimationDefintion();
}
else if (stricmp(animdefsToken, "FLAT") == 0)
{
Z_Free(animdefsToken);
P_ParseAnimationDefintion(0);
I_Error("Error parsing ANIMDEFS lump: FLats are no longer supported by Ring Racers");
}
else if (stricmp(animdefsToken, "OSCILLATE") == 0)
{
// This probably came off the tail of an earlier definition. It's technically legal syntax, but we don't support it.
I_Error("Error parsing ANIMDEFS lump: Animation definitions utilizing \"OSCILLATE\" (the animation plays in reverse when it reaches the end) are not supported by SRB2");
I_Error("Error parsing ANIMDEFS lump: Animation definitions utilizing \"OSCILLATE\" (the animation plays in reverse when it reaches the end) are not supported by Ring Racers");
}
else
{
I_Error("Error parsing ANIMDEFS lump: Expected \"TEXTURE\" or \"FLAT\", got \"%s\"",animdefsToken);
I_Error("Error parsing ANIMDEFS lump: Expected \"TEXTURE\", got \"%s\"",animdefsToken);
}
// parse next line
while (*p != '\0' && *p != '\n') ++p;
@ -310,7 +289,7 @@ void P_ParseANIMDEFSLump(INT32 wadNum, UINT16 lumpnum)
Z_Free((void *)animdefsText);
}
void P_ParseAnimationDefintion(SINT8 istexture)
void P_ParseAnimationDefintion(void)
{
char *animdefsToken;
size_t animdefsTokenLength;
@ -353,8 +332,7 @@ void P_ParseAnimationDefintion(SINT8 istexture)
// Search for existing animdef
for (i = 0; i < maxanims; i++)
if (animdefs[i].istexture == istexture // Check if it's the same type!
&& stricmp(animdefsToken, animdefs[i].startname) == 0)
if (stricmp(animdefsToken, animdefs[i].startname) == 0)
{
//CONS_Alert(CONS_NOTICE, "Duplicate animation: %s\n", animdefsToken);
@ -376,10 +354,7 @@ void P_ParseAnimationDefintion(SINT8 istexture)
Z_Free(animdefsToken);
// set texture type
if (istexture)
animdefs[i].istexture = 1;
else
animdefs[i].istexture = (animdeftempflats ? 2 : 0);
animdefs[i].istexture = 1;
// "RANGE"
animdefsToken = M_GetToken(NULL);
@ -457,16 +432,6 @@ void P_ParseAnimationDefintion(SINT8 istexture)
}
animdefs[i].speed = animSpeed;
Z_Free(animdefsToken);
#ifdef WALLFLATS
// hehe... uhh.....
if (!istexture)
{
GrowAnimDefs();
M_Memcpy(&animdefs[maxanims-1], &animdefs[i], sizeof(animdef_t));
animdefs[maxanims-1].istexture = 1;
}
#endif
}
/** Checks for flats in levelflats that are part of a flat animation sequence

View file

@ -106,10 +106,9 @@ rendermode_t chosenrendermode = render_none; // set by command line arguments
boolean highcolor = false;
static void Impl_SetVsync(void);
// synchronize page flipping with screen refresh
consvar_t cv_vidwait = CVAR_INIT ("vid_wait", "On", CV_SAVE|CV_CALL|CV_NOINIT, CV_OnOff, Impl_SetVsync);
consvar_t cv_vidwait = CVAR_INIT ("vid_wait", "On", CV_SAVE, CV_OnOff, NULL);
static consvar_t cv_stretch = CVAR_INIT ("stretch", "Off", CV_SAVE|CV_NOSHOWHELP, CV_OnOff, NULL);
static consvar_t cv_alwaysgrabmouse = CVAR_INIT ("alwaysgrabmouse", "Off", CV_SAVE, CV_OnOff, NULL);
@ -185,6 +184,20 @@ static SDL_bool Impl_CreateWindow(SDL_bool fullscreen);
//static void Impl_SetWindowName(const char *title);
static void Impl_SetWindowIcon(void);
static void Impl_SetSoftwareVsync(int vsync)
{
#if SDL_VERSION_ATLEAST(2,0,18)
static int oldvsync = 0;
if (oldvsync != vsync)
{
SDL_RenderSetVSync(renderer, vsync);
}
oldvsync = vsync;
#else
(void)vsync;
#endif
}
static void SDLSetMode(INT32 width, INT32 height, SDL_bool fullscreen, SDL_bool reposition)
{
static SDL_bool wasfullscreen = SDL_FALSE;
@ -277,6 +290,7 @@ static void SDLSetMode(INT32 width, INT32 height, SDL_bool fullscreen, SDL_bool
}
SDL_PixelFormatEnumToMasks(sw_texture_format, &bpp, &rmask, &gmask, &bmask, &amask);
vidSurface = SDL_CreateRGBSurface(0, width, height, bpp, rmask, gmask, bmask, amask);
Impl_SetSoftwareVsync(cv_vidwait.value);
}
}
@ -1254,6 +1268,7 @@ void I_FinishUpdate(void)
SDL_RenderClear(renderer);
SDL_RenderCopy(renderer, texture, &src_rect, NULL);
SDL_RenderPresent(renderer);
Impl_SetSoftwareVsync(cv_vidwait.value);
}
#ifdef HWRENDER
else if (rendermode == render_opengl)
@ -1478,15 +1493,6 @@ static SDL_bool Impl_CreateContext(void)
int flags = 0; // Use this to set SDL_RENDERER_* flags now
if (usesdl2soft)
flags |= SDL_RENDERER_SOFTWARE;
#if 0
// This shit is BROKEN.
// - The version of SDL we're using cannot toggle VSync at runtime. We'll need a new SDL version implemented to have this work properly.
// - cv_vidwait is initialized before config is loaded, so it's forced to default value at runtime, and forced off when switching. The config loading code would need restructured.
// - With both this & frame interpolation on, I_FinishUpdate takes x10 longer. At this point, it is simpler to use a standard FPS cap.
// So you can probably guess why I'm kinda over this, I'm just disabling it.
else if (cv_vidwait.value)
flags |= SDL_RENDERER_PRESENTVSYNC;
#endif
// 3 August 2022
// Possibly a Windows 11 issue; the default
@ -2006,11 +2012,3 @@ UINT32 I_GetRefreshRate(void)
// trouble querying mode over and over again.
return refresh_rate;
}
static void Impl_SetVsync(void)
{
#if SDL_VERSION_ATLEAST(2,0,18)
if (renderer)
SDL_RenderSetVSync(renderer, cv_vidwait.value);
#endif
}

View file

@ -411,12 +411,10 @@ static void ST_drawDebugInfo(void)
// Figure out some other way to display all of the RNG classes.
fixed_t peekres = P_RandomPeek(PR_UNDEFINED);
peekres *= 10000; // Change from fixed point
peekres >>= FRACBITS; // to displayable decimal
V_DrawRightAlignedString(320, height - 16, V_MONOSPACE, va("Init: %08x", P_GetInitSeed(PR_UNDEFINED)));
V_DrawRightAlignedString(320, height - 8, V_MONOSPACE, va("Seed: %08x", P_GetRandSeed(PR_UNDEFINED)));
V_DrawRightAlignedString(320, height, V_MONOSPACE, va("== : .%04d", peekres));
V_DrawRightAlignedString(320, height, V_MONOSPACE, va("== : %08x", peekres));
height -= 32;
}

View file

@ -1324,6 +1324,7 @@ lumpnum_t W_CheckNumForMap(const char *name)
lumpnum_t check = INT16_MAX;
UINT32 uhash, hash = quickncasehash(name, LUMPNUMCACHENAME);
INT32 i;
UINT16 firstfile = (partadd_earliestfile == UINT16_MAX) ? 0 : partadd_earliestfile;
// Check the lumpnumcache first. Loop backwards so that we check
// most recent entries first
@ -1339,7 +1340,7 @@ lumpnum_t W_CheckNumForMap(const char *name)
uhash = quickncasehash(name, 8); // Not a mistake, legacy system for short lumpnames
for (i = numwadfiles - 1; i >= 0; i--)
for (i = numwadfiles - 1; i >= firstfile; i--)
{
check = W_CheckNumForMapPwad(name, uhash, (UINT16)i, 0);