diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 5fe4034c0..070ab105d 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -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); diff --git a/src/d_main.c b/src/d_main.c index 20df9f9ae..bd63cd874 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -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); diff --git a/src/d_netcmd.c b/src/d_netcmd.c index a93fe0d01..a5abda657 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -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")); diff --git a/src/d_netcmd.h b/src/d_netcmd.h index 266d73395..f3ace5cd9 100644 --- a/src/d_netcmd.h +++ b/src/d_netcmd.h @@ -100,7 +100,6 @@ extern consvar_t cv_dualsneaker, cv_triplesneaker, cv_triplebanana, - cv_decabanana, cv_tripleorbinaut, cv_quadorbinaut, cv_dualjawz; diff --git a/src/d_netfil.c b/src/d_netfil.c index 3de3a0030..246567c72 100644 --- a/src/d_netfil.c +++ b/src/d_netfil.c @@ -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); diff --git a/src/d_player.h b/src/d_player.h index d61360ded..d3b6975ed 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -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 diff --git a/src/deh_soc.c b/src/deh_soc.c index 4ae9927f4..1455f0641 100644 --- a/src/deh_soc.c +++ b/src/deh_soc.c @@ -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); diff --git a/src/deh_soc.h b/src/deh_soc.h index 161422783..0d9beccab 100644 --- a/src/deh_soc.h +++ b/src/deh_soc.h @@ -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); diff --git a/src/deh_tables.c b/src/deh_tables.c index 383823833..aa9dc9310 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -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}, diff --git a/src/dehacked.c b/src/dehacked.c index 690a49dbc..99ee7b0e8 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -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) { diff --git a/src/doomdef.h b/src/doomdef.h index 0c462c8ee..8271a4ae2 100644 --- a/src/doomdef.h +++ b/src/doomdef.h @@ -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, diff --git a/src/g_demo.c b/src/g_demo.c index 0fbd920c6..7978d0ef6 100644 --- a/src/g_demo.c +++ b/src/g_demo.c @@ -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 diff --git a/src/g_game.c b/src/g_game.c index 8db8fac2c..db838ab91 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -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 diff --git a/src/hu_stuff.c b/src/hu_stuff.c index 5d73ecda6..3fbcddd7f 100644 --- a/src/hu_stuff.c +++ b/src/hu_stuff.c @@ -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 { diff --git a/src/info.c b/src/info.c index 266787230..44a4d25ae 100644 --- a/src/info.c +++ b/src/info.c @@ -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. diff --git a/src/info.h b/src/info.h index 408d85832..78fc6e2db 100644 --- a/src/info.h +++ b/src/info.h @@ -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, diff --git a/src/k_collide.c b/src/k_collide.c index b03cdfdec..d3b9bc60c 100644 --- a/src/k_collide.c +++ b/src/k_collide.c @@ -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); diff --git a/src/k_follower.c b/src/k_follower.c index 63ebda167..636620ec8 100644 --- a/src/k_follower.c +++ b/src/k_follower.c @@ -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; } diff --git a/src/k_follower.h b/src/k_follower.h index f9a16c971..3c1e2a7d1 100644 --- a/src/k_follower.h +++ b/src/k_follower.h @@ -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__ diff --git a/src/k_grandprix.c b/src/k_grandprix.c index 497909dc8..6149434c7 100644 --- a/src/k_grandprix.c +++ b/src/k_grandprix.c @@ -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; } diff --git a/src/k_hud.c b/src/k_hud.c index d4127e131..4a5644442 100644 --- a/src/k_hud.c +++ b/src/k_hud.c @@ -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; - // 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<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<width)*scale/2) : 0), - (fy<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] diff --git a/src/k_hud.h b/src/k_hud.h index 683425e8a..4f84f9dd3 100644 --- a/src/k_hud.h +++ b/src/k_hud.h @@ -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 diff --git a/src/k_kart.c b/src/k_kart.c index 583639b9c..40d1ff0a2 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -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; } } diff --git a/src/k_menu.h b/src/k_menu.h index ba4458bcc..588542002 100644 --- a/src/k_menu.h +++ b/src/k_menu.h @@ -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 diff --git a/src/k_menudef.c b/src/k_menudef.c index 66be81efb..4fb6b2262 100644 --- a/src/k_menudef.c +++ b/src/k_menudef.c @@ -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}, diff --git a/src/k_menudraw.c b/src/k_menudraw.c index 84393be4b..cfd52a459 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -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<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<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<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<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. diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 4babf145b..ab43217ff 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -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: diff --git a/src/lua_baselib.c b/src/lua_baselib.c index d73e7b073..fcdf86624 100644 --- a/src/lua_baselib.c +++ b/src/lua_baselib.c @@ -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; diff --git a/src/m_random.c b/src/m_random.c index 166c6bf2e..6d8beeaa3 100644 --- a/src/m_random.c +++ b/src/m_random.c @@ -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; } diff --git a/src/m_random.h b/src/m_random.h index c08e32993..5bb5d4435 100644 --- a/src/m_random.h +++ b/src/m_random.h @@ -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 diff --git a/src/objects/orbinaut.c b/src/objects/orbinaut.c index 4d5738e00..ee6e72ed5 100644 --- a/src/objects/orbinaut.c +++ b/src/objects/orbinaut.c @@ -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); } diff --git a/src/p_inter.c b/src/p_inter.c index 8f439ff89..e77a5d8ff 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -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)); diff --git a/src/p_map.c b/src/p_map.c index e235cb9a4..677954b43 100644 --- a/src/p_map.c +++ b/src/p_map.c @@ -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) { diff --git a/src/p_mobj.c b/src/p_mobj.c index 366be896d..d5924de51 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -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)); diff --git a/src/p_mobj.h b/src/p_mobj.h index 923fc7f80..b725f2bf0 100644 --- a/src/p_mobj.h +++ b/src/p_mobj.h @@ -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 diff --git a/src/p_setup.c b/src/p_setup.c index 5bbdb53ab..efadfedd9 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -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; +} diff --git a/src/p_setup.h b/src/p_setup.h index d5d3aa7d0..04a3233e0 100644 --- a/src/p_setup.h +++ b/src/p_setup.h @@ -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); diff --git a/src/p_spec.c b/src/p_spec.c index 91b958e31..31af5b0e6 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -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 diff --git a/src/sdl/i_video.c b/src/sdl/i_video.c index a1948f2f1..5e97694ad 100644 --- a/src/sdl/i_video.c +++ b/src/sdl/i_video.c @@ -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 -} diff --git a/src/st_stuff.c b/src/st_stuff.c index 676c3992a..5c2def212 100644 --- a/src/st_stuff.c +++ b/src/st_stuff.c @@ -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; } diff --git a/src/w_wad.c b/src/w_wad.c index 8edbe74b6..e81879114 100644 --- a/src/w_wad.c +++ b/src/w_wad.c @@ -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);