diff --git a/src/doomstat.h b/src/doomstat.h index 04930eff5..0d763a5a9 100644 --- a/src/doomstat.h +++ b/src/doomstat.h @@ -45,6 +45,10 @@ extern INT32 cursaveslot; extern INT16 lastmaploaded; extern boolean gamecomplete; +#define maxgameovers 13 +extern UINT8 numgameovers; +extern SINT8 startinglivesbalance[maxgameovers+1]; + #define PRECIP_NONE 0 #define PRECIP_STORM 1 #define PRECIP_SNOW 2 diff --git a/src/f_finale.c b/src/f_finale.c index 89df14467..05c3d5fc0 100644 --- a/src/f_finale.c +++ b/src/f_finale.c @@ -1125,7 +1125,7 @@ void F_StartCredits(void) M_ClearMenus(true); // Save the second we enter the credits - if ((!modifiedgame || savemoddata) && !(netgame || multiplayer) && cursaveslot >= 0) + if ((!modifiedgame || savemoddata) && !(netgame || multiplayer) && cursaveslot > 0) G_SaveGame((UINT32)cursaveslot); if (creditscutscene) @@ -1263,7 +1263,7 @@ static boolean drawemblem = false, drawchaosemblem = false; void F_StartGameEvaluation(void) { // Credits option in secrets menu - if (cursaveslot == -2) + if (cursaveslot == -1) { F_StartGameEnd(); return; @@ -1277,7 +1277,7 @@ void F_StartGameEvaluation(void) // Save the second we enter the evaluation // We need to do this again! Remember, it's possible a mod designed skipped // the credits sequence! - if ((!modifiedgame || savemoddata) && !(netgame || multiplayer) && cursaveslot >= 0) + if ((!modifiedgame || savemoddata) && !(netgame || multiplayer) && cursaveslot > 0) G_SaveGame((UINT32)cursaveslot); gameaction = ga_nothing; diff --git a/src/g_game.c b/src/g_game.c index 96908de73..60304d802 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -76,11 +76,14 @@ INT16 gamemap = 1; INT16 maptol; UINT8 globalweather = 0; INT32 curWeather = PRECIP_NONE; -INT32 cursaveslot = -1; // Auto-save 1p savegame slot +INT32 cursaveslot = 0; // Auto-save 1p savegame slot //INT16 lastmapsaved = 0; // Last map we auto-saved at INT16 lastmaploaded = 0; // Last map the game loaded boolean gamecomplete = false; +UINT8 numgameovers = 0; // for startinglives balance +SINT8 startinglivesbalance[maxgameovers+1] = {3, 5, 7, 9, 12, 15, 20, 25, 30, 40, 50, 75, 99, 0x7F}; + UINT16 mainwads = 0; boolean modifiedgame; // Set if homebrew PWAD stuff has been added. boolean savemoddata = false; @@ -3149,8 +3152,11 @@ static void G_DoContinued(void) tokenlist = 0; token = 0; + if (!(netgame || multiplayer || demoplayback || demorecording || metalrecording || modeattacking) && (!modifiedgame || savemoddata) && cursaveslot > 0) + G_SaveGameOver((UINT32)cursaveslot, true); + // Reset # of lives - pl->lives = (ultimatemode) ? 1 : 3; + pl->lives = (ultimatemode) ? 1 : startinglivesbalance[numgameovers]; D_MapChange(gamemap, gametype, ultimatemode, false, 0, false, false); @@ -3489,59 +3495,6 @@ void G_SaveGameData(void) #define VERSIONSIZE 16 -#ifdef SAVEGAMES_OTHERVERSIONS -static INT16 startonmapnum = 0; - -// -// User wants to load a savegame from a different version? -// -static void M_ForceLoadGameResponse(INT32 ch) -{ - if (ch != 'y' && ch != KEY_ENTER) - { - //refused - Z_Free(savebuffer); - save_p = savebuffer = NULL; - startonmapnum = 0; - M_SetupNextMenu(&SP_LoadDef); - return; - } - - // pick up where we left off. - save_p += VERSIONSIZE; - if (!P_LoadGame(startonmapnum)) - { - M_ClearMenus(true); // so ESC backs out to title - M_StartMessage(M_GetText("Savegame file corrupted\n\nPress ESC\n"), NULL, MM_NOTHING); - Command_ExitGame_f(); - Z_Free(savebuffer); - save_p = savebuffer = NULL; - startonmapnum = 0; - - // no cheating! - memset(&savedata, 0, sizeof(savedata)); - return; - } - - // done - Z_Free(savebuffer); - save_p = savebuffer = NULL; - startonmapnum = 0; - - //set cursaveslot to -1 so nothing gets saved. - cursaveslot = -1; - - displayplayer = consoleplayer; - multiplayer = splitscreen = false; - - if (setsizeneeded) - R_ExecuteSetViewSize(); - - M_ClearMenus(true); - CON_ToggleOff(); -} -#endif - // // G_InitFromSavegame // Can be called by the startup code or the menu task. @@ -3634,13 +3587,13 @@ void G_LoadGame(UINT32 slot, INT16 mapoverride) // G_SaveGame // Saves your game. // -void G_SaveGame(UINT32 savegameslot) +void G_SaveGame(UINT32 slot) { boolean saved; char savename[256] = ""; const char *backup; - sprintf(savename, savegamename, savegameslot); + sprintf(savename, savegamename, slot); backup = va("%s",savename); // save during evaluation or credits? game's over, folks! @@ -3676,9 +3629,91 @@ void G_SaveGame(UINT32 savegameslot) if (cv_debug && saved) CONS_Printf(M_GetText("Game saved.\n")); else if (!saved) - CONS_Alert(CONS_ERROR, M_GetText("Error while writing to %s for save slot %u, base: %s\n"), backup, savegameslot, savegamename); + CONS_Alert(CONS_ERROR, M_GetText("Error while writing to %s for save slot %u, base: %s\n"), backup, slot, savegamename); } +#define BADSAVE goto cleanup; +#define CHECKPOS if (save_p >= end_p) BADSAVE +void G_SaveGameOver(UINT32 slot, boolean modifylives) +{ + boolean saved = false; + size_t length; + char vcheck[VERSIONSIZE]; + char savename[255]; + const char *backup; + + sprintf(savename, savegamename, slot); + backup = va("%s",savename); + + length = FIL_ReadFile(savename, &savebuffer); + if (!length) + { + CONS_Printf(M_GetText("Couldn't read file %s\n"), savename); + return; + } + + { + char temp[sizeof(timeattackfolder)]; + UINT8 *end_p = savebuffer + length; + UINT8 *lives_p; + SINT8 pllives; + + save_p = savebuffer; + // Version check + memset(vcheck, 0, sizeof (vcheck)); + sprintf(vcheck, "version %d", VERSION); + if (strcmp((const char *)save_p, (const char *)vcheck)) BADSAVE + save_p += VERSIONSIZE; + + // P_UnArchiveMisc() + (void)READINT16(save_p); + CHECKPOS + (void)READUINT16(save_p); // emeralds + CHECKPOS + READSTRINGN(save_p, temp, sizeof(temp)); // mod it belongs to + if (strcmp(temp, timeattackfolder)) BADSAVE + + // P_UnArchivePlayer() + CHECKPOS + (void)READUINT16(save_p); + CHECKPOS + + WRITEUINT8(save_p, numgameovers); + CHECKPOS + + lives_p = save_p; + pllives = READSINT8(save_p); // lives + CHECKPOS + if (modifylives && pllives < startinglivesbalance[numgameovers]) + { + pllives = startinglivesbalance[numgameovers]; + WRITESINT8(lives_p, pllives); + } + + (void)READINT32(save_p); // Score + CHECKPOS + (void)READINT32(save_p); // continues + + // File end marker check + CHECKPOS + if (READUINT8(save_p) != 0x1d) BADSAVE; + + // done + saved = FIL_WriteFile(backup, savebuffer, length); + } + +cleanup: + if (cv_debug && saved) + CONS_Printf(M_GetText("Game saved.\n")); + else if (!saved) + CONS_Alert(CONS_ERROR, M_GetText("Error while writing to %s for save slot %u, base: %s\n"), backup, slot, savegamename); + Z_Free(savebuffer); + save_p = savebuffer = NULL; + +} +#undef CHECKPOS +#undef BADSAVE + // // G_DeferedInitNew // Can be called by the startup code or the menu task, @@ -3686,7 +3721,7 @@ void G_SaveGame(UINT32 savegameslot) // void G_DeferedInitNew(boolean pultmode, const char *mapname, INT32 pickedchar, boolean SSSG, boolean FLS) { - UINT8 color = 0; + UINT8 color = skins[pickedchar].prefcolor; paused = false; if (demoplayback) @@ -3698,10 +3733,8 @@ void G_DeferedInitNew(boolean pultmode, const char *mapname, INT32 pickedchar, b if (savedata.lives > 0) { - color = savedata.skincolor; - botskin = savedata.botskin; - botcolor = savedata.botcolor; - botingame = (botskin != 0); + if ((botingame = ((botskin = savedata.botskin) != 0))) + botcolor = skins[botskin-1].prefcolor; } else if (splitscreen != SSSG) { @@ -3709,8 +3742,7 @@ void G_DeferedInitNew(boolean pultmode, const char *mapname, INT32 pickedchar, b SplitScreen_OnChange(); } - if (!color) - color = skins[pickedchar].prefcolor; + color = skins[pickedchar].prefcolor; SetPlayerSkinByNum(consoleplayer, pickedchar); CV_StealthSet(&cv_skin, skins[pickedchar].name); CV_StealthSetValue(&cv_playercolor, color); @@ -3742,7 +3774,7 @@ void G_InitNew(UINT8 pultmode, const char *mapname, boolean resetplayer, boolean if (resetplayer) { // Clear a bunch of variables - tokenlist = token = sstimer = redscore = bluescore = lastmap = 0; + numgameovers = tokenlist = token = sstimer = redscore = bluescore = lastmap = 0; countdown = countdown2 = 0; for (i = 0; i < MAXPLAYERS; i++) @@ -3757,15 +3789,10 @@ void G_InitNew(UINT8 pultmode, const char *mapname, boolean resetplayer, boolean players[i].lives = cv_startinglives.value; players[i].continues = 0; } - else if (pultmode) - { - players[i].lives = 1; - players[i].continues = 0; - } else { - players[i].lives = 3; - players[i].continues = 1; + players[i].lives = (pultmode) ? 1 : startinglivesbalance[0]; + players[i].continues = (pultmode) ? 0 : 1; } if (!((netgame || multiplayer) && (FLS))) diff --git a/src/g_game.h b/src/g_game.h index 72a6f3d6e..de04e2846 100644 --- a/src/g_game.h +++ b/src/g_game.h @@ -116,6 +116,8 @@ void G_SaveGameData(void); void G_SaveGame(UINT32 slot); +void G_SaveGameOver(UINT32 slot, boolean modifylives); + // Only called by startup code. void G_RecordDemo(const char *name); void G_RecordMetal(void); diff --git a/src/hu_stuff.c b/src/hu_stuff.c index e92b96995..b49d3eb96 100644 --- a/src/hu_stuff.c +++ b/src/hu_stuff.c @@ -83,6 +83,7 @@ patch_t *rmatcico; patch_t *bmatcico; patch_t *tagico; patch_t *tallminus; +patch_t *tallinfin; //------------------------------------------- // coop hud @@ -235,6 +236,7 @@ void HU_LoadGraphics(void) // minus for negative tallnums tallminus = (patch_t *)W_CachePatchName("STTMINUS", PU_HUDGFX); + tallinfin = (patch_t *)W_CachePatchName("STTINFIN", PU_HUDGFX); // cache the crosshairs, don't bother to know which one is being used, // just cache all 3, they're so small anyway. @@ -1250,7 +1252,7 @@ void HU_DrawTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scorelines, I } } - if (G_GametypeUsesLives() && !(gametype == GT_COOP && (cv_cooplives.value == 0 || cv_cooplives.value == 3))) //show lives + if (G_GametypeUsesLives() && !(gametype == GT_COOP && (cv_cooplives.value == 0 || cv_cooplives.value == 3)) && (players[tab[i].num].lives != 0x7f)) //show lives V_DrawRightAlignedString(x, y+4, V_ALLOWLOWERCASE|(greycheck ? V_60TRANS : 0), va("%dx", players[tab[i].num].lives)); else if (G_TagGametype() && players[tab[i].num].pflags & PF_TAGIT) { @@ -1388,7 +1390,7 @@ void HU_DrawDualTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scoreline | (greycheck ? V_TRANSLUCENT : 0) | V_ALLOWLOWERCASE, name); - if (G_GametypeUsesLives() && !(gametype == GT_COOP && (cv_cooplives.value == 0 || cv_cooplives.value == 3))) //show lives + if (G_GametypeUsesLives() && !(gametype == GT_COOP && (cv_cooplives.value == 0 || cv_cooplives.value == 3)) && (players[tab[i].num].lives != 0x7f)) //show lives V_DrawRightAlignedString(x, y+4, V_ALLOWLOWERCASE, va("%dx", players[tab[i].num].lives)); else if (G_TagGametype() && players[tab[i].num].pflags & PF_TAGIT) V_DrawSmallScaledPatch(x-28, y-4, 0, tagico); diff --git a/src/hu_stuff.h b/src/hu_stuff.h index e757db85a..2dbeb556d 100644 --- a/src/hu_stuff.h +++ b/src/hu_stuff.h @@ -71,6 +71,7 @@ extern patch_t *rmatcico; extern patch_t *bmatcico; extern patch_t *tagico; extern patch_t *tallminus; +extern patch_t *tallinfin; extern patch_t *tokenicon; // set true when entering a chat message diff --git a/src/m_cheat.c b/src/m_cheat.c index f988c0fd5..7650e0742 100644 --- a/src/m_cheat.c +++ b/src/m_cheat.c @@ -61,7 +61,7 @@ static UINT8 cheatf_ultimate(void) if (menuactive && (currentMenu != &MainDef && currentMenu != &SP_LoadDef)) return 0; // Only on the main menu, or the save select! - S_StartSound(0, sfx_itemup); + BwehHehHe(); ultimate_selectable = (!ultimate_selectable); // If on the save select, move to what is now Ultimate Mode! diff --git a/src/m_menu.c b/src/m_menu.c index 64255e71a..b983546cc 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -172,7 +172,9 @@ static char joystickInfo[8][25]; static UINT32 serverlistpage; #endif -static saveinfo_t savegameinfo[MAXSAVEGAMES]; // Extra info about the save games. +static UINT8 numsaves = 0; +static saveinfo_t* savegameinfo = NULL; // Extra info about the save games. +static patch_t *savselp[7]; INT16 startmap; // Mario, NiGHTS, or just a plain old normal game? @@ -222,7 +224,7 @@ static INT32 lsoffs[2]; #define lshli levelselectselect[2] #define lshseperation 101 -#define lsbasevseperation 62 +#define lsbasevseperation (62*vid.height)/(BASEVIDHEIGHT*vid.dupy) //62 #define lsheadingheight 16 #define getheadingoffset(row) (levelselect.rows[row].header[0] ? lsheadingheight : 0) #define lsvseperation(row) lsbasevseperation + getheadingoffset(row) @@ -2580,7 +2582,7 @@ boolean M_Responder(event_t *ev) { if (((currentMenu->menuitems[itemOn].status & IT_CALLTYPE) & IT_CALL_NOTMODIFIED) && modifiedgame && !savemoddata) { - S_StartSound(NULL, sfx_menu1); + S_StartSound(NULL, sfx_skid); M_StartMessage(M_GetText("This cannot be done in a modified game.\n\n(Press a key)\n"), NULL, MM_NOTHING); return true; } @@ -4061,10 +4063,10 @@ static boolean M_PrepareLevelPlatter(INT32 gt) if (actnum) sprintf(mapname, "%s %d", mapheaderinfo[mapnum]->lvlttl, actnum); else - sprintf(mapname, "%s", mapheaderinfo[mapnum]->lvlttl); + strcpy(mapname, mapheaderinfo[mapnum]->lvlttl); if (strlen(mapname) >= 17) - sprintf(mapname+17-3, "..."); + strcpy(mapname+17-3, "..."); strcpy(levelselect.rows[row].mapnames[col], (const char *)mapname); } @@ -4093,6 +4095,24 @@ static boolean M_PrepareLevelPlatter(INT32 gt) mapnum++; } +#ifdef SYMMETRICAL_PLATTER + // horizontally space out rows with missing right sides + for (; row >= 0; row--) + { + if (!levelselect.rows[row].maplist[2] // no right side + && levelselect.rows[row].maplist[0] && levelselect.rows[row].maplist[1]) // all the left filled in + { + levelselect.rows[row].maplist[2] = levelselect.rows[row].maplist[1]; + STRBUFCPY(levelselect.rows[row].mapnames[2], levelselect.rows[row].mapnames[1]); + levelselect.rows[row].mapavailable[2] = levelselect.rows[row].mapavailable[1]; + + levelselect.rows[row].maplist[1] = -1; // diamond + levelselect.rows[row].mapnames[1][0] = '\0'; + levelselect.rows[row].mapavailable[1] = false; + } + } +#endif + if (levselp[0][0]) // never going to have some provided but not all, saves individually checking { W_UnlockCachedPatch(levselp[0][0]); @@ -4230,7 +4250,7 @@ static void M_HandleLevelPlatter(INT32 choice) else if (!lsoffs[0]) // prevent sound spam { lsoffs[0] = -8; - S_StartSound(NULL,sfx_s3kb2); + S_StartSound(NULL,sfx_lose); } break; @@ -4261,14 +4281,10 @@ void M_DrawLevelPlatterHeader(INT32 y, const char *header, boolean headerhighlig y += lsheadingheight - 12; V_DrawString(19, y, (headerhighlight ? V_YELLOWMAP : 0)|(allowlowercase ? V_ALLOWLOWERCASE : 0), header); y += 9; - if ((y >= 0) && (y < 200)) - { - V_DrawFill(19, y, 281, 1, (headerhighlight ? yellowmap[3] : 3)); - V_DrawFill(300, y, 1, 1, 26); - } + V_DrawFill(19, y, 281, 1, (headerhighlight ? yellowmap[3] : 3)); + V_DrawFill(300, y, 1, 1, 26); y++; - if ((y >= 0) && (y < 200)) - V_DrawFill(19, y, 282, 1, 26); + V_DrawFill(19, y, 282, 1, 26); } static void M_DrawLevelPlatterWideMap(UINT8 row, UINT8 col, INT32 x, INT32 y, boolean highlight) @@ -4276,7 +4292,7 @@ static void M_DrawLevelPlatterWideMap(UINT8 row, UINT8 col, INT32 x, INT32 y, bo patch_t *patch; INT32 map = levelselect.rows[row].maplist[col]; - if (!map) + if (map <= 0) return; // A 564x100 image of the level as entry MAPxxW @@ -4292,22 +4308,9 @@ static void M_DrawLevelPlatterWideMap(UINT8 row, UINT8 col, INT32 x, INT32 y, bo V_DrawSmallScaledPatch(x, y, 0, patch); } - if ((y+50) < 200) - { - INT32 topy = (y+50), h = 8; - - if (topy < 0) - { - h += topy; - topy = 0; - } - else if (topy + h >= 200) - h = 200 - y; - if (h > 0) - V_DrawFill(x, topy, 282, h, - ((mapheaderinfo[map-1]->unlockrequired < 0) - ? 159 : 63)); - } + V_DrawFill(x, y+50, 282, 8, + ((mapheaderinfo[map-1]->unlockrequired < 0) + ? 159 : 63)); V_DrawString(x, y+50, (highlight ? V_YELLOWMAP : 0), levelselect.rows[row].mapnames[col]); } @@ -4317,7 +4320,7 @@ static void M_DrawLevelPlatterMap(UINT8 row, UINT8 col, INT32 x, INT32 y, boolea patch_t *patch; INT32 map = levelselect.rows[row].maplist[col]; - if (!map) + if (map <= 0) return; // A 160x100 image of the level as entry MAPxxP @@ -4333,22 +4336,9 @@ static void M_DrawLevelPlatterMap(UINT8 row, UINT8 col, INT32 x, INT32 y, boolea V_DrawSmallScaledPatch(x, y, 0, patch); } - if ((y+50) < 200) - { - INT32 topy = (y+50), h = 8; - - if (topy < 0) - { - h += topy; - topy = 0; - } - else if (topy + h >= 200) - h = 200 - y; - if (h > 0) - V_DrawFill(x, topy, 80, h, - ((mapheaderinfo[map-1]->unlockrequired < 0) - ? 159 : 63)); - } + V_DrawFill(x, y+50, 80, 8, + ((mapheaderinfo[map-1]->unlockrequired < 0) + ? 159 : 63)); if (strlen(levelselect.rows[row].mapnames[col]) > 6) // "AERIAL GARDEN" vs "ACT 18" - "THE ACT" intentionally compressed V_DrawThinString(x, y+50, (highlight ? V_YELLOWMAP : 0), levelselect.rows[row].mapnames[col]); @@ -4402,16 +4392,16 @@ static void M_DrawLevelPlatterMenu(void) // draw cursor box V_DrawSmallScaledPatch(lsbasex + cursorx + lsoffs[1], lsbasey, 0, (levselp[sizeselect][((skullAnimCounter/4) ? 1 : 0)])); - if (levelselect.rows[lsrow].maplist[lscol]) + if (levelselect.rows[lsrow].maplist[lscol] > 0) V_DrawScaledPatch(lsbasex + cursorx-17, lsbasey+50+lsoffs[0], 0, W_CachePatchName("M_CURSOR", PU_CACHE)); // handle movement of cursor box - if (abs(lsoffs[0]) > 1) + if (lsoffs[0] > 1 || lsoffs[0] < -1) lsoffs[0] = 2*lsoffs[0]/3; else lsoffs[0] = 0; - if (abs(lsoffs[1]) > 1) + if (lsoffs[1] > 1 || lsoffs[1] < -1) lsoffs[1] = 2*lsoffs[1]/3; else lsoffs[1] = 0; @@ -5395,7 +5385,7 @@ static void M_LevelSelectWarp(INT32 choice) G_LoadGame((UINT32)cursaveslot, startmap); else { - cursaveslot = -1; + cursaveslot = 0; M_SetupChoosePlayer(0); } } @@ -5970,7 +5960,7 @@ static void M_CustomWarp(INT32 choice) static void M_Credits(INT32 choice) { (void)choice; - cursaveslot = -2; + cursaveslot = -1; M_ClearMenus(true); F_StartCredits(); } @@ -6028,149 +6018,285 @@ static void M_LoadGameLevelSelect(INT32 choice) // LOAD GAME MENU // ============== -static INT32 saveSlotSelected = 0; -static short menumovedir = 0; +static INT32 saveSlotSelected = 1; +static INT32 loadgamescroll = 0; +static UINT8 loadgameoffset = 0; static void M_DrawLoadGameData(void) { - INT32 ecks; - INT32 i; + INT32 i, savetodraw, x, y, hsep = 90; + skin_t *charskin = NULL; - ecks = SP_LoadDef.x + 24; - M_DrawTextBox(SP_LoadDef.x-12,144, 24, 4); + if (vid.width != BASEVIDWIDTH*vid.dupx) + hsep = (hsep*vid.width)/(BASEVIDWIDTH*vid.dupx); - if (saveSlotSelected == NOSAVESLOT) // last slot is play without saving + for (i = -2; i <= 2; i++) { - if (ultimate_selectable) + savetodraw = (saveSlotSelected + i + numsaves)%numsaves; + x = (BASEVIDWIDTH/2 - 42 + loadgamescroll) + (i*hsep); + y = 33 + 9; + { - V_DrawCenteredString(ecks + 68, 144, V_ORANGEMAP, "ULTIMATE MODE"); - V_DrawCenteredString(ecks + 68, 156, 0, "NO RINGS, NO ONE-UPS,"); - V_DrawCenteredString(ecks + 68, 164, 0, "NO CONTINUES, ONE LIFE,"); - V_DrawCenteredString(ecks + 68, 172, 0, "FINAL DESTINATION."); + INT32 diff = x - (BASEVIDWIDTH/2 - 42); + if (diff < 0) + diff = -diff; + diff = (42 - diff)/3 - loadgameoffset; + if (diff < 0) + diff = 0; + y -= diff; } - else - { - V_DrawCenteredString(ecks + 68, 144, V_ORANGEMAP, "PLAY WITHOUT SAVING"); - V_DrawCenteredString(ecks + 68, 156, 0, "THIS GAME WILL NOT BE"); - V_DrawCenteredString(ecks + 68, 164, 0, "SAVED, BUT YOU CAN STILL"); - V_DrawCenteredString(ecks + 68, 172, 0, "GET EMBLEMS AND SECRETS."); - } - return; - } - - if (savegameinfo[saveSlotSelected].lives == -42) // Empty - { - V_DrawCenteredString(ecks + 68, 160, 0, "NO DATA"); - return; - } - - if (savegameinfo[saveSlotSelected].lives == -666) // savegame is bad - { - V_DrawCenteredString(ecks + 68, 144, V_REDMAP, "CORRUPT SAVE FILE"); - V_DrawCenteredString(ecks + 68, 156, 0, "THIS SAVE FILE"); - V_DrawCenteredString(ecks + 68, 164, 0, "CAN NOT BE LOADED."); - V_DrawCenteredString(ecks + 68, 172, 0, "DELETE USING BACKSPACE."); - return; - } - - // Draw the back sprite, it looks ugly if we don't - V_DrawScaledPatch(SP_LoadDef.x, 144+8, 0, livesback); - if (savegameinfo[saveSlotSelected].skincolor == 0) - V_DrawScaledPatch(SP_LoadDef.x,144+8,0,W_CachePatchName(skins[savegameinfo[saveSlotSelected].skinnum].face, PU_CACHE)); - else - { - UINT8 *colormap = R_GetTranslationColormap(savegameinfo[saveSlotSelected].skinnum, savegameinfo[saveSlotSelected].skincolor, 0); - V_DrawMappedPatch(SP_LoadDef.x,144+8,0,W_CachePatchName(skins[savegameinfo[saveSlotSelected].skinnum].face, PU_CACHE), colormap); - } - - V_DrawString(ecks + 12, 152, 0, savegameinfo[saveSlotSelected].playername); - -#ifdef SAVEGAMES_OTHERVERSIONS - if (savegameinfo[saveSlotSelected].gamemap & 16384) - V_DrawCenteredString(ecks + 68, 144, V_REDMAP, "OUTDATED SAVE FILE!"); -#endif - - if (savegameinfo[saveSlotSelected].gamemap & 8192) - V_DrawString(ecks + 12, 160, V_GREENMAP, "CLEAR!"); - else - V_DrawString(ecks + 12, 160, 0, va("%s", savegameinfo[saveSlotSelected].levelname)); - - // Use the big face pic for lives, duh. :3 - V_DrawScaledPatch(ecks + 12, 175, 0, W_CachePatchName("STLIVEX", PU_HUDGFX)); - V_DrawTallNum(ecks + 40, 172, 0, savegameinfo[saveSlotSelected].lives); - - // Absolute ridiculousness, condensed into another function. - V_DrawContinueIcon(ecks + 58, 182, 0, savegameinfo[saveSlotSelected].skinnum, savegameinfo[saveSlotSelected].skincolor); - V_DrawScaledPatch(ecks + 68, 175, 0, W_CachePatchName("STLIVEX", PU_HUDGFX)); - V_DrawTallNum(ecks + 96, 172, 0, savegameinfo[saveSlotSelected].continues); - - for (i = 0; i < 7; ++i) - { - if (savegameinfo[saveSlotSelected].numemeralds & (1 << i)) - V_DrawScaledPatch(ecks + 104 + (i * 8), 172, 0, tinyemeraldpics[i]); - } -} - -#define LOADBARHEIGHT SP_LoadDef.y + (LINEHEIGHT * (j+1)) + ymod -#define CURSORHEIGHT SP_LoadDef.y + (LINEHEIGHT*3) - 1 -static void M_DrawLoad(void) -{ - INT32 i, j; - INT32 ymod = 0, offset = 0; - - M_DrawMenuTitle(); - - if (menumovedir != 0) //movement illusion - { - ymod = (-(LINEHEIGHT/4))*menumovedir; - offset = ((menumovedir > 0) ? -1 : 1); - } - - V_DrawCenteredString(BASEVIDWIDTH/2, 40, 0, "Press backspace to delete a save."); - - for (i = MAXSAVEGAMES + saveSlotSelected - 2 + offset, j = 0;i <= MAXSAVEGAMES + saveSlotSelected + 2 + offset; i++, j++) - { - if ((menumovedir < 0 && j == 4) || (menumovedir > 0 && j == 0)) - continue; //this helps give the illusion of movement - - M_DrawSaveLoadBorder(SP_LoadDef.x, LOADBARHEIGHT); - - if ((i%MAXSAVEGAMES) == NOSAVESLOT) // play without saving + + if (savetodraw == 0) { + V_DrawSmallScaledPatch(x, y, 0, + savselp[((ultimate_selectable) ? 2 : 1)]); + x += 2; + y += 1; + V_DrawString(x, y, + ((savetodraw == saveSlotSelected) ? V_YELLOWMAP : 0), + "NO FILE"); + if (savetodraw == saveSlotSelected) + V_DrawFill(x, y+9, 80, 1, yellowmap[3]); + y += 11; + V_DrawSmallScaledPatch(x, y, V_STATIC, savselp[4]); + y += 41; if (ultimate_selectable) - V_DrawCenteredString(SP_LoadDef.x+92, LOADBARHEIGHT - 1, V_ORANGEMAP, "ULTIMATE MODE"); + V_DrawRightAlignedThinString(x + 79, y, V_REDMAP, "ULTIMATE."); else - V_DrawCenteredString(SP_LoadDef.x+92, LOADBARHEIGHT - 1, V_ORANGEMAP, "PLAY WITHOUT SAVING"); + V_DrawRightAlignedThinString(x + 79, y, V_GRAYMAP, "DON'T SAVE!"); continue; } - if (savegameinfo[i%MAXSAVEGAMES].lives == -42) - V_DrawString(SP_LoadDef.x-6, LOADBARHEIGHT - 1, V_TRANSLUCENT, "NO DATA"); - else if (savegameinfo[i%MAXSAVEGAMES].lives == -666) - V_DrawString(SP_LoadDef.x-6, LOADBARHEIGHT - 1, V_REDMAP, "CORRUPT SAVE FILE"); - else if (savegameinfo[i%MAXSAVEGAMES].gamemap & 8192) - V_DrawString(SP_LoadDef.x-6, LOADBARHEIGHT - 1, V_GREENMAP, "CLEAR!"); - else - V_DrawString(SP_LoadDef.x-6, LOADBARHEIGHT - 1, 0, va("%s", savegameinfo[i%MAXSAVEGAMES].levelname)); + savetodraw--; - //Draw the save slot number on the right side - V_DrawRightAlignedString(SP_LoadDef.x+192, LOADBARHEIGHT - 1, 0, va("%d",(i%MAXSAVEGAMES) + 1)); + if (savegameinfo[savetodraw].lives > 0) + charskin = &skins[savegameinfo[savetodraw].skinnum]; + + // signpost background + { + UINT8 col; + if (savegameinfo[savetodraw].lives == -666) + { + V_DrawSmallScaledPatch(x+2, y+64, 0, savselp[5]); + } +#ifndef PERFECTSAVE // disabled, don't touch + else if ((savegameinfo[savetodraw].skinnum == 1) + && (savegameinfo[savetodraw].lives == 99) + && (savegameinfo[savetodraw].gamemap & 8192) + && (savegameinfo[savetodraw].numgameovers == 0) + && (savegameinfo[savetodraw].numemeralds == ((1<<7) - 1))) // perfect save + { + V_DrawFill(x+6, y+64, 72, 50, 134); + V_DrawFill(x+6, y+74, 72, 30, 201); + V_DrawFill(x+6, y+84, 72, 10, 1); + } +#endif + else + { + if (savegameinfo[savetodraw].lives == -42) + col = 26; + else if (savegameinfo[savetodraw].botskin == 3) // & knuckles + col = 105; + else if (savegameinfo[savetodraw].botskin) // tailsbot or custom + col = 134; + else + { + col = (charskin->prefcolor - 1)*2; + col = Color_Index[Color_Opposite[col]-1][Color_Opposite[col+1]]; + } + + V_DrawFill(x+6, y+64, 72, 50, col); + } + } + + V_DrawSmallScaledPatch(x, y, 0, savselp[0]); + x += 2; + y += 1; + V_DrawString(x, y, + ((savetodraw == saveSlotSelected-1) ? V_YELLOWMAP : 0), + va("FILE %d", savetodraw+1)); + if (savetodraw == saveSlotSelected-1) + V_DrawFill(x, y+9, 80, 1, yellowmap[3]); + y += 11; + + // level image area + { + patch_t *patch; + INT32 flags = 0; + + if ((savegameinfo[savetodraw].lives == -42) + || (savegameinfo[savetodraw].lives == -666)) + { + patch = savselp[3]; + flags = V_STATIC; + } + else if (savegameinfo[savetodraw].gamemap & 8192) + patch = savselp[6]; + else + { + lumpnum_t lumpnum = W_CheckNumForName(va("%sP", G_BuildMapName((savegameinfo[savetodraw].gamemap) & 8191))); + if (lumpnum != LUMPERROR) + patch = W_CachePatchNum(lumpnum, PU_CACHE); + else + patch = savselp[5]; + } + + V_DrawSmallScaledPatch(x, y, flags, patch); + + y += 41; + + if (savegameinfo[savetodraw].lives == -42) + V_DrawRightAlignedThinString(x + 79, y, V_GRAYMAP, "NEW GAME"); + else if (savegameinfo[savetodraw].lives == -666) + V_DrawRightAlignedThinString(x + 79, y, V_REDMAP, "CAN'T LOAD!"); + else if (savegameinfo[savetodraw].gamemap & 8192) + V_DrawRightAlignedThinString(x + 79, y, V_GREENMAP, "CLEAR!"); + else + V_DrawRightAlignedThinString(x + 79, y, V_YELLOWMAP, savegameinfo[savetodraw].levelname); + } + + if ((savegameinfo[savetodraw].lives == -42) + || (savegameinfo[savetodraw].lives == -666)) + continue; + + y += 64; + + // tiny emeralds + { + INT32 j, workx = x + 6; + for (j = 0; j < 7; ++j) + { + if (savegameinfo[savetodraw].numemeralds & (1 << j)) + V_DrawScaledPatch(workx, y, 0, tinyemeraldpics[j]); + workx += 10; + } + } + + y -= 13; + + // character heads, lives, and continues + { + spritedef_t *sprdef; + spriteframe_t *sprframe; + patch_t *patch; + UINT8 *colormap = NULL; + + INT32 tempx = (x+40)<sprites[SPR2_SIGN]; + if (!sprdef->numframes) + goto skipbot; + colormap = R_GetTranslationColormap(savegameinfo[savetodraw].botskin, charbotskin->prefcolor, 0); + sprframe = &sprdef->spriteframes[0]; + patch = W_CachePatchNum(sprframe->lumppat[0], PU_CACHE); + + V_DrawFixedPatch( + tempx + (18<highresscale, + 0, patch, colormap); + + Z_Free(colormap); + + tempx -= (15<sprites[SPR2_SIGN]; + colormap = R_GetTranslationColormap(savegameinfo[savetodraw].skinnum, charskin->prefcolor, 0); + if (!sprdef->numframes) + goto skipsign; + sprframe = &sprdef->spriteframes[0]; + patch = W_CachePatchNum(sprframe->lumppat[0], PU_CACHE); + if ((calc = SHORT(patch->topoffset) - 42) > 0) + tempy += ((4+calc)<highresscale, + flip, patch, colormap); + +skipsign: + y += 25; + + tempx = x + 10; + if (savegameinfo[savetodraw].lives != 0x7f + && savegameinfo[savetodraw].lives > 9) + tempx -= 4; + + if (!charskin) // shut up compiler + goto skiplife; + + // lives + sprdef = &charskin->sprites[SPR2_LIFE]; + if (!sprdef->numframes) + goto skiplife; + sprframe = &sprdef->spriteframes[0]; + patch = W_CachePatchNum(sprframe->lumppat[0], PU_CACHE); + + V_DrawFixedPatch( + (tempx + 4)<highresscale/2, + 0, patch, colormap); +skiplife: + if (colormap) + Z_Free(colormap); + + patch = W_CachePatchName("STLIVEX", PU_CACHE); + + V_DrawScaledPatch(tempx + 9, y + 2, 0, patch); + tempx += 16; + if (savegameinfo[savetodraw].lives == 0x7f) + V_DrawCharacter(tempx, y + 1, '\x16', false); + else + V_DrawString(tempx, y, 0, va("%d", savegameinfo[savetodraw].lives)); + + tempx = x + 47; + if (savegameinfo[savetodraw].continues > 9) + tempx -= 4; + + // continues + if (savegameinfo[savetodraw].continues > 0) + { + V_DrawSmallScaledPatch(tempx, y, 0, W_CachePatchName("CONTSAVE", PU_CACHE)); + V_DrawScaledPatch(tempx + 9, y + 2, 0, patch); + V_DrawString(tempx + 16, y, 0, va("%d", savegameinfo[savetodraw].continues)); + } + else + { + V_DrawSmallScaledPatch(tempx, y, 0, W_CachePatchName("CONTNONE", PU_CACHE)); + V_DrawScaledPatch(tempx + 9, y + 2, 0, W_CachePatchName("STNONEX", PU_CACHE)); + V_DrawString(tempx + 16, y, V_GRAYMAP, "0"); + } + } } +} - //Draw cursors on both sides. - V_DrawScaledPatch( 32, CURSORHEIGHT, 0, W_CachePatchName("M_CURSOR", PU_CACHE)); - V_DrawScaledPatch(274, CURSORHEIGHT, 0, W_CachePatchName("M_CURSOR", PU_CACHE)); +static void M_DrawLoad(void) +{ + M_DrawMenuTitle(); + + if (loadgamescroll > 1 || loadgamescroll < -1) + loadgamescroll = 2*loadgamescroll/3; + else + loadgamescroll = 0; + + if (loadgameoffset > 1) + loadgameoffset = 2*loadgameoffset/3; + else + loadgameoffset = 0; M_DrawLoadGameData(); - - //finishing the movement illusion - if (menumovedir) - menumovedir += ((menumovedir > 0) ? 1 : -1); - if (abs(menumovedir) > 3) - menumovedir = 0; } -#undef LOADBARHEIGHT -#undef CURSORHEIGHT // // User wants to load this game @@ -6182,7 +6308,7 @@ static void M_LoadSelect(INT32 choice) if (saveSlotSelected == NOSAVESLOT) //last slot is play without saving { M_NewGame(); - cursaveslot = -1; + cursaveslot = 0; return; } @@ -6191,8 +6317,8 @@ static void M_LoadSelect(INT32 choice) // This slot is empty, so start a new game here. M_NewGame(); } - else if (savegameinfo[saveSlotSelected].gamemap & 8192) // Completed - M_LoadGameLevelSelect(saveSlotSelected + 1); + else if (savegameinfo[saveSlotSelected-1].gamemap & 8192) // Completed + M_LoadGameLevelSelect(0); else G_LoadGame((UINT32)saveSlotSelected, 0); @@ -6214,12 +6340,11 @@ static void M_ReadSavegameInfo(UINT32 slot) INT32 fake; // Dummy variable char temp[sizeof(timeattackfolder)]; char vcheck[VERSIONSIZE]; -#ifdef SAVEGAMES_OTHERVERSIONS - boolean oldversion = false; -#endif sprintf(savename, savegamename, slot); + slot--; + length = FIL_ReadFile(savename, &savebuffer); if (length == 0) { @@ -6235,14 +6360,7 @@ static void M_ReadSavegameInfo(UINT32 slot) // Version check memset(vcheck, 0, sizeof (vcheck)); sprintf(vcheck, "version %d", VERSION); - if (strcmp((const char *)save_p, (const char *)vcheck)) - { -#ifdef SAVEGAMES_OTHERVERSIONS - oldversion = true; -#else - BADSAVE // Incompatible versions? -#endif - } + if (strcmp((const char *)save_p, (const char *)vcheck)) BADSAVE save_p += VERSIONSIZE; // dearchive all the modifications @@ -6254,30 +6372,19 @@ static void M_ReadSavegameInfo(UINT32 slot) if (((fake-1) & 8191) >= NUMMAPS) BADSAVE if(!mapheaderinfo[(fake-1) & 8191]) - { savegameinfo[slot].levelname[0] = '\0'; - savegameinfo[slot].actnum = 0; - } else { - strcpy(savegameinfo[slot].levelname, mapheaderinfo[(fake-1) & 8191]->lvlttl); - savegameinfo[slot].actnum = mapheaderinfo[(fake-1) & 8191]->actnum; + strlcpy(savegameinfo[slot].levelname, mapheaderinfo[(fake-1) & 8191]->lvlttl, 17+1); + + if (strlen(mapheaderinfo[(fake-1) & 8191]->lvlttl) >= 17) + strcpy(savegameinfo[slot].levelname+17-3, "..."); } -#ifdef SAVEGAMES_OTHERVERSIONS - if (oldversion) - { - if (fake == 24) //meh, let's count old Clear! saves too - fake |= 8192; - fake |= 16384; // marker for outdated version - } -#endif savegameinfo[slot].gamemap = fake; CHECKPOS - fake = READUINT16(save_p)-357; // emeralds - - savegameinfo[slot].numemeralds = (UINT8)fake; + savegameinfo[slot].numemeralds = READUINT16(save_p)-357; // emeralds CHECKPOS READSTRINGN(save_p, temp, sizeof(temp)); // mod it belongs to @@ -6286,39 +6393,25 @@ static void M_ReadSavegameInfo(UINT32 slot) // P_UnArchivePlayer() CHECKPOS - savegameinfo[slot].skincolor = READUINT8(save_p); - CHECKPOS - savegameinfo[slot].skinnum = READUINT8(save_p); + fake = READUINT16(save_p); + savegameinfo[slot].skinnum = fake & ((1<<5) - 1); + if (savegameinfo[slot].skinnum >= numskins + || !R_SkinUsable(-1, savegameinfo[slot].skinnum)) + BADSAVE + savegameinfo[slot].botskin = fake >> 5; + if (savegameinfo[slot].botskin-1 >= numskins + || !R_SkinUsable(-1, savegameinfo[slot].botskin-1)) + BADSAVE + CHECKPOS + savegameinfo[slot].numgameovers = READUINT8(save_p); // numgameovers + CHECKPOS + savegameinfo[slot].lives = READSINT8(save_p); // lives CHECKPOS (void)READINT32(save_p); // Score - - CHECKPOS - savegameinfo[slot].lives = READINT32(save_p); // lives CHECKPOS savegameinfo[slot].continues = READINT32(save_p); // continues - if (fake & (1<<10)) - { - CHECKPOS - savegameinfo[slot].botskin = READUINT8(save_p); - if (savegameinfo[slot].botskin-1 >= numskins) - savegameinfo[slot].botskin = 0; - CHECKPOS - savegameinfo[slot].botcolor = READUINT8(save_p); // because why not. - } - else - savegameinfo[slot].botskin = 0; - - if (savegameinfo[slot].botskin) - snprintf(savegameinfo[slot].playername, 32, "%s & %s", - skins[savegameinfo[slot].skinnum].realname, - skins[savegameinfo[slot].botskin-1].realname); - else - strcpy(savegameinfo[slot].playername, skins[savegameinfo[slot].skinnum].realname); - - savegameinfo[slot].playername[31] = 0; - // File end marker check CHECKPOS if (READUINT8(save_p) != 0x1d) BADSAVE; @@ -6337,23 +6430,80 @@ static void M_ReadSavegameInfo(UINT32 slot) static void M_ReadSaveStrings(void) { FILE *handle; - UINT32 i; + SINT8 i; char name[256]; + boolean nofile[MAXSAVEGAMES-1]; + SINT8 tolerance = 3; // empty slots at any time + UINT8 lastseen = 0; - for (i = 0; i < MAXSAVEGAMES; i++) + loadgamescroll = 0; + loadgameoffset = 14; + + for (i = 1; (i < MAXSAVEGAMES); i++) // slot 0 is no save { snprintf(name, sizeof name, savegamename, i); name[sizeof name - 1] = '\0'; handle = fopen(name, "rb"); - if (handle == NULL) + if ((nofile[i-1] = (handle == NULL))) + continue; + fclose(handle); + lastseen = i; + } + + if (savegameinfo) + Z_Free(savegameinfo); + savegameinfo = NULL; + + if (lastseen < saveSlotSelected) + lastseen = saveSlotSelected; + + i = lastseen; + + for (; (lastseen > 0 && tolerance); lastseen--) + { + if (nofile[lastseen-1]) + tolerance--; + } + + if ((i += tolerance+1) > MAXSAVEGAMES) // show 3 empty slots at minimum + i = MAXSAVEGAMES; + + numsaves = i; + savegameinfo = Z_Realloc(savegameinfo, numsaves*sizeof(saveinfo_t), PU_STATIC, NULL); + if (!savegameinfo) + I_Error("Insufficient memory to prepare save platter"); + + for (; i > 0; i--) + { + if (nofile[i-1] == true) { - savegameinfo[i].lives = -42; + savegameinfo[i-1].lives = -42; continue; } - fclose(handle); M_ReadSavegameInfo(i); } + + if (savselp[0]) // never going to have some provided but not all, saves individually checking + { + W_UnlockCachedPatch(savselp[0]); + W_UnlockCachedPatch(savselp[1]); + W_UnlockCachedPatch(savselp[2]); + + W_UnlockCachedPatch(savselp[3]); + W_UnlockCachedPatch(savselp[4]); + W_UnlockCachedPatch(savselp[5]); + W_UnlockCachedPatch(savselp[6]); + } + + savselp[0] = W_CachePatchName("SAVEBACK", PU_STATIC); + savselp[1] = W_CachePatchName("SAVENONE", PU_STATIC); + savselp[2] = W_CachePatchName("ULTIMATE", PU_STATIC); + + savselp[3] = W_CachePatchName("BLACKLVL", PU_STATIC); + savselp[4] = W_CachePatchName("BLACXLVL", PU_STATIC); + savselp[5] = W_CachePatchName("BLANKLVL", PU_STATIC); + savselp[6] = W_CachePatchName("GAMEDONE", PU_STATIC); } // @@ -6371,8 +6521,19 @@ static void M_SaveGameDeleteResponse(INT32 ch) name[sizeof name - 1] = '\0'; remove(name); - // Refresh savegame menu info - M_ReadSaveStrings(); + BwehHehHe(); + M_ReadSaveStrings(); // reload the menu +} + +static void M_SaveGameUltimateResponse(INT32 ch) +{ + if (ch != 'y' && ch != KEY_ENTER) + return; + + S_StartSound(NULL, sfx_menu1); + M_LoadSelect(saveSlotSelected); + SP_PlayerDef.prevMenu = MessageDef.prevMenu; + MessageDef.prevMenu = &SP_PlayerDef; } static void M_HandleLoadSave(INT32 choice) @@ -6381,26 +6542,46 @@ static void M_HandleLoadSave(INT32 choice) switch (choice) { - case KEY_DOWNARROW: - S_StartSound(NULL, sfx_menu1); + case KEY_RIGHTARROW: + S_StartSound(NULL, sfx_s3kb7); ++saveSlotSelected; - if (saveSlotSelected >= MAXSAVEGAMES) - saveSlotSelected -= MAXSAVEGAMES; - menumovedir = 1; + if (saveSlotSelected >= numsaves) + saveSlotSelected -= numsaves; + loadgamescroll = 90; break; - case KEY_UPARROW: - S_StartSound(NULL, sfx_menu1); + case KEY_LEFTARROW: + S_StartSound(NULL, sfx_s3kb7); --saveSlotSelected; if (saveSlotSelected < 0) - saveSlotSelected += MAXSAVEGAMES; - menumovedir = -1; + saveSlotSelected += numsaves; + loadgamescroll = -90; break; case KEY_ENTER: - S_StartSound(NULL, sfx_menu1); - if (savegameinfo[saveSlotSelected].lives != -666) // don't allow loading of "bad saves" + if (ultimate_selectable && saveSlotSelected == NOSAVESLOT) + { + loadgamescroll = 0; + S_StartSound(NULL, sfx_skid); + M_StartMessage("Are you sure you want to play\n\x85ultimate mode\x80? It isn't remotely fair,\nand you don't even get an emblem for it.\n\n(Press 'Y' to confirm)\n",M_SaveGameUltimateResponse,MM_YESNO); + } + else if (saveSlotSelected != NOSAVESLOT && savegameinfo[saveSlotSelected-1].lives == -42 && !(!modifiedgame || savemoddata)) + { + loadgamescroll = 0; + S_StartSound(NULL, sfx_skid); + M_StartMessage(M_GetText("This cannot be done in a modified game.\n\n(Press a key)\n"), NULL, MM_NOTHING); + } + else if (saveSlotSelected == NOSAVESLOT || savegameinfo[saveSlotSelected-1].lives != -666) // don't allow loading of "bad saves" + { + loadgamescroll = 0; + S_StartSound(NULL, sfx_menu1); M_LoadSelect(saveSlotSelected); + } + else if (!loadgameoffset) + { + S_StartSound(NULL, sfx_lose); + loadgameoffset = 14; + } break; case KEY_ESCAPE: @@ -6408,11 +6589,25 @@ static void M_HandleLoadSave(INT32 choice) break; case KEY_BACKSPACE: - S_StartSound(NULL, sfx_menu1); // Don't allow people to 'delete' "Play without Saving." // Nor allow people to 'delete' slots with no saves in them. - if (saveSlotSelected != NOSAVESLOT && savegameinfo[saveSlotSelected].lives != -42) - M_StartMessage(M_GetText("Are you sure you want to delete\nthis save game?\n\n(Press 'Y' to confirm)\n"),M_SaveGameDeleteResponse,MM_YESNO); + if (saveSlotSelected != NOSAVESLOT && savegameinfo[saveSlotSelected-1].lives != -42) + { + loadgamescroll = 0; + S_StartSound(NULL, sfx_skid); + M_StartMessage(va("Are you sure you want to delete\nsave file %d?\n\n(Press 'Y' to confirm)\n", saveSlotSelected),M_SaveGameDeleteResponse,MM_YESNO); + } + else if (!loadgameoffset) + { + if (saveSlotSelected == NOSAVESLOT && ultimate_selectable) + { + ultimate_selectable = false; + S_StartSound(NULL, sfx_strpst); + } + else + S_StartSound(NULL, sfx_lose); + loadgameoffset = 14; + } break; } if (exitmenu) @@ -6421,6 +6616,8 @@ static void M_HandleLoadSave(INT32 choice) M_SetupNextMenu(currentMenu->prevMenu); else M_ClearMenus(true); + Z_Free(savegameinfo); + savegameinfo = NULL; } } @@ -6440,14 +6637,15 @@ static void M_LoadGame(INT32 choice) // void M_ForceSaveSlotSelected(INT32 sslot) { - // Already there? Out of bounds? Whatever, then! - if (sslot == saveSlotSelected || sslot >= MAXSAVEGAMES) + loadgameoffset = 14; + + // Already there? Whatever, then! + if (sslot == saveSlotSelected) return; - // Figure out whether to display up movement or down movement - menumovedir = (saveSlotSelected - sslot) > 0 ? -1 : 1; - if (abs(saveSlotSelected - sslot) > (MAXSAVEGAMES>>1)) - menumovedir *= -1; + loadgamescroll = 90; + if (saveSlotSelected <= numsaves/2) + loadgamescroll = -loadgamescroll; saveSlotSelected = sslot; } @@ -6718,7 +6916,7 @@ static void M_ChoosePlayer(INT32 choice) } if (startmap != spstage_start) - cursaveslot = -1; + cursaveslot = 0; //lastmapsaved = 0; gamecomplete = false; @@ -6729,6 +6927,10 @@ static void M_ChoosePlayer(INT32 choice) if (levelselect.rows) Z_Free(levelselect.rows); levelselect.rows = NULL; + + if (savegameinfo) + Z_Free(savegameinfo); + savegameinfo = NULL; } // =============== @@ -8687,7 +8889,7 @@ static void M_EraseDataResponse(INT32 ch) totalplaytime = 0; F_StartIntro(); } - S_StartSound(NULL, sfx_bewar1+M_RandomKey(4)); // Bweh heh he + BwehHehHe(); M_ClearMenus(true); } diff --git a/src/m_menu.h b/src/m_menu.h index 53dc266d1..8040b63e6 100644 --- a/src/m_menu.h +++ b/src/m_menu.h @@ -215,18 +215,14 @@ typedef struct // savegame struct for save game menu typedef struct { - char playername[32]; char levelname[32]; - UINT8 actnum; - UINT8 skincolor; UINT8 skinnum; UINT8 botskin; - UINT8 botcolor; UINT8 numemeralds; + UINT8 numgameovers; INT32 lives; INT32 continues; INT32 gamemap; - UINT8 netgame; } saveinfo_t; extern description_t description[32]; @@ -238,7 +234,9 @@ extern INT16 startmap; extern INT32 ultimate_selectable; #define MAXSAVEGAMES 31 //note: last save game is "no save" -#define NOSAVESLOT MAXSAVEGAMES-1 //slot where Play Without Saving appears +#define NOSAVESLOT 0 //slot where Play Without Saving appears + +#define BwehHehHe() S_StartSound(NULL, sfx_bewar1+M_RandomKey(4)) // Bweh heh he void M_ForceSaveSlotSelected(INT32 sslot); diff --git a/src/p_inter.c b/src/p_inter.c index 783f3229c..f74c2c3bc 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -2239,7 +2239,7 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget if ((target->player->lives <= 1) && (netgame || multiplayer) && (gametype == GT_COOP) && (cv_cooplives.value == 0)) ; - else if (!target->player->bot && !target->player->spectator && !G_IsSpecialStage(gamemap) + else if (!target->player->bot && !target->player->spectator && !G_IsSpecialStage(gamemap) && (target->player->lives != 0x7f) && G_GametypeUsesLives()) { target->player->lives -= 1; // Lose a life Tails 03-11-2000 @@ -2269,6 +2269,13 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget S_StopMusic(); // Stop the Music! Tails 03-14-2000 S_ChangeMusicInternal("_gover", false); // Yousa dead now, Okieday? Tails 03-14-2000 } + + if (!(netgame || multiplayer || demoplayback || demorecording || metalrecording || modeattacking) && numgameovers < maxgameovers) + { + numgameovers++; + if ((!modifiedgame || savemoddata) && cursaveslot > 0) + G_SaveGameOver((UINT32)cursaveslot, (target->player->continues <= 0)); + } } } target->player->playerstate = PST_DEAD; diff --git a/src/p_saveg.c b/src/p_saveg.c index 2d3412e65..840f546af 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -64,22 +64,16 @@ typedef enum static inline void P_ArchivePlayer(void) { const player_t *player = &players[consoleplayer]; - INT32 pllives = player->lives; - if (pllives < 3) // Bump up to 3 lives if the player - pllives = 3; // has less than that. - - WRITEUINT8(save_p, player->skincolor); - WRITEUINT8(save_p, player->skin); + INT16 skininfo = player->skin + (botskin<<5); + SINT8 pllives = player->lives; + if (pllives < startinglivesbalance[numgameovers]) // Bump up to 3 lives if the player + pllives = startinglivesbalance[numgameovers]; // has less than that. + WRITEUINT16(save_p, skininfo); + WRITEUINT8(save_p, numgameovers); + WRITESINT8(save_p, pllives); WRITEUINT32(save_p, player->score); - WRITEINT32(save_p, pllives); WRITEINT32(save_p, player->continues); - - if (botskin) - { - WRITEUINT8(save_p, botskin); - WRITEUINT8(save_p, botcolor); - } } // @@ -87,22 +81,14 @@ static inline void P_ArchivePlayer(void) // static inline void P_UnArchivePlayer(void) { - savedata.skincolor = READUINT8(save_p); - savedata.skin = READUINT8(save_p); + INT16 skininfo = READUINT16(save_p); + savedata.skin = skininfo & ((1<<5) - 1); + savedata.botskin = skininfo >> 5; - savedata.score = READINT32(save_p); - savedata.lives = READINT32(save_p); + savedata.numgameovers = READUINT8(save_p); + savedata.lives = READSINT8(save_p); + savedata.score = READUINT32(save_p); savedata.continues = READINT32(save_p); - - if (savedata.botcolor) - { - savedata.botskin = READUINT8(save_p); - if (savedata.botskin-1 >= numskins) - savedata.botskin = 0; - savedata.botcolor = READUINT8(save_p); - } - else - savedata.botskin = 0; } // @@ -3162,7 +3148,7 @@ static inline void P_ArchiveMisc(void) //lastmapsaved = gamemap; lastmaploaded = gamemap; - WRITEUINT16(save_p, (botskin ? (emeralds|(1<<10)) : emeralds)+357); + WRITEUINT16(save_p, emeralds+357); WRITESTRINGN(save_p, timeattackfolder, sizeof(timeattackfolder)); } @@ -3192,9 +3178,6 @@ static inline void P_UnArchiveSPGame(INT16 mapoverride) token = 0; savedata.emeralds = READUINT16(save_p)-357; - if (savedata.emeralds & (1<<10)) - savedata.botcolor = 0xFF; - savedata.emeralds &= 0xff; READSTRINGN(save_p, testname, sizeof(testname)); diff --git a/src/p_saveg.h b/src/p_saveg.h index 3670d3503..5960660ab 100644 --- a/src/p_saveg.h +++ b/src/p_saveg.h @@ -30,14 +30,13 @@ mobj_t *P_FindNewPosition(UINT32 oldposition); typedef struct { - UINT8 skincolor; UINT8 skin; UINT8 botskin; - UINT8 botcolor; INT32 score; INT32 lives; INT32 continues; UINT16 emeralds; + UINT8 numgameovers; } savedata_t; extern savedata_t savedata; diff --git a/src/p_setup.c b/src/p_setup.c index 20c212212..e409d61f6 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -3021,19 +3021,19 @@ boolean P_SetupLevel(boolean skipprecip) P_RunCachedActions(); if (!(netgame || multiplayer || demoplayback || demorecording || metalrecording || modeattacking || players[consoleplayer].lives <= 0) - && (!modifiedgame || savemoddata) && cursaveslot >= 0 && CanSaveLevel(gamemap)) + && (!modifiedgame || savemoddata) && cursaveslot > 0 && CanSaveLevel(gamemap)) G_SaveGame((UINT32)cursaveslot); lastmaploaded = gamemap; // HAS to be set after saving!! if (savedata.lives > 0) { + numgameovers = savedata.numgameovers; players[consoleplayer].continues = savedata.continues; players[consoleplayer].lives = savedata.lives; players[consoleplayer].score = savedata.score; - botskin = savedata.botskin; - botcolor = savedata.botcolor; - botingame = (savedata.botskin != 0); + if ((botingame = ((botskin = savedata.botskin) != 0))) + botcolor = skins[botskin-1].prefcolor; emeralds = savedata.emeralds; savedata.lives = 0; } @@ -3202,8 +3202,8 @@ boolean P_AddWadFile(const char *wadfilename, char **firstmapname) ST_Start(); // Prevent savefile cheating - if (cursaveslot >= 0) - cursaveslot = -1; + if (cursaveslot > 0) + cursaveslot = 0; if (replacedcurrentmap && gamestate == GS_LEVEL && (netgame || multiplayer)) { diff --git a/src/p_user.c b/src/p_user.c index 956f6fdd7..f4e686fd3 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -950,6 +950,8 @@ void P_GivePlayerRings(player_t *player, INT32 num_rings) // void P_GivePlayerLives(player_t *player, INT32 numlives) { + if (player->lives == 0x7f) return; + player->lives += numlives; if (player->lives > 99) @@ -1153,7 +1155,9 @@ void P_PlayLivesJingle(player_t *player) if (player && !P_IsLocalPlayer(player)) return; - if (gametype == GT_COOP && (netgame || multiplayer) && cv_cooplives.value == 0) + if ((player && player->lives == 0x7f) + || (!player && &players[consoleplayer] && players[consoleplayer].lives == 0x7f) + || (gametype == GT_COOP && (netgame || multiplayer) && cv_cooplives.value == 0)) S_StartSound(NULL, sfx_lose); else if (use1upSound) S_StartSound(NULL, sfx_oneup); @@ -8160,7 +8164,8 @@ boolean P_GetLives(player_t *player) INT32 i, maxlivesplayer = -1, livescheck = 1; if (!(netgame || multiplayer) || (gametype != GT_COOP) - || (cv_cooplives.value == 1)) + || (cv_cooplives.value == 1) + || (player->lives == 0x7f)) return true; if ((cv_cooplives.value == 2 || cv_cooplives.value == 0) && player->lives > 0) @@ -8187,7 +8192,8 @@ boolean P_GetLives(player_t *player) { if (cv_cooplives.value == 2 && (P_IsLocalPlayer(player) || P_IsLocalPlayer(&players[maxlivesplayer]))) S_StartSound(NULL, sfx_jshard); // placeholder - players[maxlivesplayer].lives--; + if (players[maxlivesplayer].lives != 0x7f) + players[maxlivesplayer].lives--; player->lives++; if (player->lives < 1) player->lives = 1; diff --git a/src/st_stuff.c b/src/st_stuff.c index 4515495af..ceef586a4 100644 --- a/src/st_stuff.c +++ b/src/st_stuff.c @@ -684,6 +684,8 @@ static inline void ST_drawRings(void) static void ST_drawLives(void) { const INT32 v_splitflag = (splitscreen && stplyr == &players[displayplayer] ? V_SPLITSCREEN : 0); + INT32 livescount; + boolean notgreyedout; if (!stplyr->skincolor) return; // Just joined a server, skin isn't loaded yet! @@ -723,66 +725,47 @@ static void ST_drawLives(void) V_SNAPTOLEFT|V_SNAPTOBOTTOM|V_HUDTRANS|v_splitflag, stlivex); // lives number - if ((netgame || multiplayer) && gametype == GT_COOP) + if ((netgame || multiplayer) && gametype == GT_COOP && cv_cooplives.value == 3) { - switch (cv_cooplives.value) + INT32 i; + livescount = 0; + notgreyedout = (stplyr->lives > 0); + for (i = 0; i < MAXPLAYERS; i++) { - case 0: - V_DrawCharacter(hudinfo[HUD_LIVESNUM].x - 8, hudinfo[HUD_LIVESNUM].y + (v_splitflag ? -4 : 0), '\x16' | 0x80 | V_SNAPTOLEFT|V_SNAPTOBOTTOM|V_HUDTRANS|v_splitflag, false); - return; - case 3: - { - INT32 i, sum = 0; - boolean canrespawn = (stplyr->lives > 0); - for (i = 0; i < MAXPLAYERS; i++) - { - if (!playeringame[i]) - continue; + if (!playeringame[i]) + continue; - if (players[i].lives < 1) - continue; + if (players[i].lives < 1) + continue; - if (players[i].lives > 1) - canrespawn = true; + if (players[i].lives > 1) + notgreyedout = true; - sum += (players[i].lives); - } - V_DrawRightAlignedString(hudinfo[HUD_LIVESNUM].x, hudinfo[HUD_LIVESNUM].y + (v_splitflag ? -4 : 0), - V_SNAPTOLEFT|V_SNAPTOBOTTOM|(canrespawn ? V_HUDTRANS : V_HUDTRANSHALF)|v_splitflag, - va("%d",sum)); - return; - } -#if 0 // render the number of lives you COULD steal - case 2: - { - INT32 i, sum = 0; - for (i = 0; i < MAXPLAYERS; i++) - { - if (!playeringame[i]) - continue; - - if (&players[i] == stplyr) - continue; - - if (players[i].lives < 2) - continue; - - sum += (players[i].lives - 1); - } - V_DrawString(hudinfo[HUD_LIVESNUM].x, hudinfo[HUD_LIVESNUM].y + (v_splitflag ? -4 : 0), - V_SNAPTOLEFT|V_SNAPTOBOTTOM|V_HUDTRANSHALF|v_splitflag, va("/%d",sum)); - } - // intentional fallthrough -#endif - default: - // don't return so the SP one can be drawn below + if (players[i].lives == 0x7f) + { + livescount = 0x7f; break; + } + else if (livescount < 99) + livescount += (players[i].lives); } } + else + { + livescount = stplyr->lives; + notgreyedout = true; + } - V_DrawRightAlignedString(hudinfo[HUD_LIVESNUM].x, hudinfo[HUD_LIVESNUM].y + (v_splitflag ? -4 : 0), - V_SNAPTOLEFT|V_SNAPTOBOTTOM|V_HUDTRANS|v_splitflag, - va("%d",stplyr->lives)); + if (livescount == 0x7f) + V_DrawCharacter(hudinfo[HUD_LIVESNUM].x - 8, hudinfo[HUD_LIVESNUM].y + (v_splitflag ? -4 : 0), '\x16' | 0x80 | V_SNAPTOLEFT|V_SNAPTOBOTTOM|V_HUDTRANS|v_splitflag, false); + else + { + if (livescount > 99) + livescount = 99; + V_DrawRightAlignedString(hudinfo[HUD_LIVESNUM].x, hudinfo[HUD_LIVESNUM].y + (v_splitflag ? -4 : 0), + V_SNAPTOLEFT|V_SNAPTOBOTTOM|(notgreyedout ? V_HUDTRANS : V_HUDTRANSHALF)|v_splitflag, + ((livescount > 99) ? "!!" : va("%d",livescount))); + } } static void ST_drawLevelTitle(void) diff --git a/src/v_video.c b/src/v_video.c index 6ac101f2d..b0d8fc52b 100644 --- a/src/v_video.c +++ b/src/v_video.c @@ -524,12 +524,21 @@ static inline UINT8 transmappedpdraw(const UINT8 *dest, const UINT8 *source, fix { return *(v_translevel + (((*(v_colormap + source[ofs>>FRACBITS]))<<8)&0xff00) + (*dest&0xff)); } + +static UINT8 staticstep = 0; +static fixed_t staticval = 0; + static inline UINT8 staticpdraw(const UINT8 *dest, const UINT8 *source, fixed_t ofs) { UINT8 val = source[ofs>>FRACBITS]; (void)dest; + if ((++staticstep) >= 4) + { + staticstep = 0; + staticval = M_RandomFixed(); + } if (val < 7) return val; - return M_RandomKey(7+1)+(val-7);//M_RandomByte(); + return ((staticval>>staticstep)&7)+(val-7); } // Draws a patch scaled to arbitrary size. @@ -660,30 +669,10 @@ void V_DrawFixedPatch(fixed_t x, fixed_t y, fixed_t pscale, INT32 scrn, patch_t y = FixedMul(y,dupy<>= FRACBITS; y >>= FRACBITS; - desttop += (y*vid.width) + x; // Center it if necessary if (!(scrn & V_SCALEPATCHMASK)) { - if (vid.width != BASEVIDWIDTH * dupx) - { - // dupx adjustments pretend that screen width is BASEVIDWIDTH * dupx, - // so center this imaginary screen - if (scrn & V_SNAPTORIGHT) - desttop += (vid.width - (BASEVIDWIDTH * dupx)); - else if (!(scrn & V_SNAPTOLEFT)) - desttop += (vid.width - (BASEVIDWIDTH * dupx)) / 2; - } - if (vid.height != BASEVIDHEIGHT * dupy) - { - // same thing here - if ((scrn & (V_SPLITSCREEN|V_SNAPTOBOTTOM)) == (V_SPLITSCREEN|V_SNAPTOBOTTOM)) - desttop += (vid.height/2 - (BASEVIDHEIGHT/2 * dupy)) * vid.width; - else if (scrn & V_SNAPTOBOTTOM) - desttop += (vid.height - (BASEVIDHEIGHT * dupy)) * vid.width; - else if (!(scrn & V_SNAPTOTOP)) - desttop += (vid.height - (BASEVIDHEIGHT * dupy)) * vid.width / 2; - } // if it's meant to cover the whole screen, black out the rest if (x == 0 && SHORT(patch->width) == BASEVIDWIDTH && y == 0 && SHORT(patch->height) == BASEVIDHEIGHT) { @@ -691,7 +680,29 @@ void V_DrawFixedPatch(fixed_t x, fixed_t y, fixed_t pscale, INT32 scrn, patch_t source = (const UINT8 *)(column) + 3; V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, (column->topdelta == 0xff ? 31 : source[0])); } + + if (vid.width != BASEVIDWIDTH * dupx) + { + // dupx adjustments pretend that screen width is BASEVIDWIDTH * dupx, + // so center this imaginary screen + if (scrn & V_SNAPTORIGHT) + x += (vid.width - (BASEVIDWIDTH * dupx)); + else if (!(scrn & V_SNAPTOLEFT)) + x += (vid.width - (BASEVIDWIDTH * dupx)) / 2; + } + if (vid.height != BASEVIDHEIGHT * dupy) + { + // same thing here + if ((scrn & (V_SPLITSCREEN|V_SNAPTOBOTTOM)) == (V_SPLITSCREEN|V_SNAPTOBOTTOM)) + y += (vid.height/2 - (BASEVIDHEIGHT/2 * dupy)); + else if (scrn & V_SNAPTOBOTTOM) + y += (vid.height - (BASEVIDHEIGHT * dupy)); + else if (!(scrn & V_SNAPTOTOP)) + y += (vid.height - (BASEVIDHEIGHT * dupy)) / 2; + } } + + desttop += (y*vid.width) + x; } if (pscale != FRACUNIT) // scale width properly @@ -797,28 +808,10 @@ void V_DrawCroppedPatch(fixed_t x, fixed_t y, fixed_t pscale, INT32 scrn, patch_ y = FixedMul(y,dupy<>= FRACBITS; y >>= FRACBITS; - desttop += (y*vid.width) + x; // Center it if necessary if (!(scrn & V_SCALEPATCHMASK)) { - if (vid.width != BASEVIDWIDTH * dupx) - { - // dupx adjustments pretend that screen width is BASEVIDWIDTH * dupx, - // so center this imaginary screen - if (scrn & V_SNAPTORIGHT) - desttop += (vid.width - (BASEVIDWIDTH * dupx)); - else if (!(scrn & V_SNAPTOLEFT)) - desttop += (vid.width - (BASEVIDWIDTH * dupx)) / 2; - } - if (vid.height != BASEVIDHEIGHT * dupy) - { - // same thing here - if (scrn & V_SNAPTOBOTTOM) - desttop += (vid.height - (BASEVIDHEIGHT * dupy)) * vid.width; - else if (!(scrn & V_SNAPTOTOP)) - desttop += (vid.height - (BASEVIDHEIGHT * dupy)) * vid.width / 2; - } // if it's meant to cover the whole screen, black out the rest if (x == 0 && SHORT(patch->width) == BASEVIDWIDTH && y == 0 && SHORT(patch->height) == BASEVIDHEIGHT) { @@ -826,7 +819,29 @@ void V_DrawCroppedPatch(fixed_t x, fixed_t y, fixed_t pscale, INT32 scrn, patch_ source = (const UINT8 *)(column) + 3; V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, (column->topdelta == 0xff ? 31 : source[0])); } + + if (vid.width != BASEVIDWIDTH * dupx) + { + // dupx adjustments pretend that screen width is BASEVIDWIDTH * dupx, + // so center this imaginary screen + if (scrn & V_SNAPTORIGHT) + x += (vid.width - (BASEVIDWIDTH * dupx)); + else if (!(scrn & V_SNAPTOLEFT)) + x += (vid.width - (BASEVIDWIDTH * dupx)) / 2; + } + if (vid.height != BASEVIDHEIGHT * dupy) + { + // same thing here + if ((scrn & (V_SPLITSCREEN|V_SNAPTOBOTTOM)) == (V_SPLITSCREEN|V_SNAPTOBOTTOM)) + y += (vid.height/2 - (BASEVIDHEIGHT/2 * dupy)); + else if (scrn & V_SNAPTOBOTTOM) + y += (vid.height - (BASEVIDHEIGHT * dupy)); + else if (!(scrn & V_SNAPTOTOP)) + y += (vid.height - (BASEVIDHEIGHT * dupy)) / 2; + } } + + desttop += (y*vid.width) + x; } for (col = sx<>FRACBITS) < SHORT(patch->width) && (col>>FRACBITS) < w; col += colfrac, ++x, desttop++) @@ -1346,7 +1361,7 @@ char *V_WordWrap(INT32 x, INT32 w, INT32 option, const char *string) // void V_DrawString(INT32 x, INT32 y, INT32 option, const char *string) { - INT32 w, c, cx = x, cy = y, dupx, dupy, scrwidth = BASEVIDWIDTH, center = 0; + INT32 w, c, cx = x, cy = y, dupx, dupy, scrwidth, center = 0, left = 0; const char *ch = string; INT32 charflags = 0; const UINT8 *colormap = NULL; @@ -1362,7 +1377,11 @@ void V_DrawString(INT32 x, INT32 y, INT32 option, const char *string) scrwidth = vid.width; } else + { dupx = dupy = 1; + scrwidth = vid.width/vid.dupx; + left = (scrwidth - BASEVIDWIDTH)/2; + } charflags = (option & V_CHARCOLORMASK); @@ -1422,9 +1441,9 @@ void V_DrawString(INT32 x, INT32 y, INT32 option, const char *string) else w = SHORT(hu_font[c]->width) * dupx; - if (cx + w > scrwidth) + if (cx+left > scrwidth) break; - if (cx < 0) //left boundary check + if (cx+left + w < 0) //left boundary check { cx += w; continue; @@ -1455,7 +1474,7 @@ void V_DrawRightAlignedString(INT32 x, INT32 y, INT32 option, const char *string // void V_DrawSmallString(INT32 x, INT32 y, INT32 option, const char *string) { - INT32 w, c, cx = x, cy = y, dupx, dupy, scrwidth = BASEVIDWIDTH, center = 0; + INT32 w, c, cx = x, cy = y, dupx, dupy, scrwidth, center = 0, left = 0; const char *ch = string; INT32 charflags = 0; const UINT8 *colormap = NULL; @@ -1471,7 +1490,11 @@ void V_DrawSmallString(INT32 x, INT32 y, INT32 option, const char *string) scrwidth = vid.width; } else + { dupx = dupy = 1; + scrwidth = vid.width/vid.dupx; + left = (scrwidth - BASEVIDWIDTH)/2; + } charflags = (option & V_CHARCOLORMASK); @@ -1529,9 +1552,9 @@ void V_DrawSmallString(INT32 x, INT32 y, INT32 option, const char *string) } else w = SHORT(hu_font[c]->width) * dupx / 2; - if (cx + w > scrwidth) + if (cx+left > scrwidth) break; - if (cx < 0) //left boundary check + if (cx+left + w < 0) //left boundary check { cx += w; continue; @@ -1556,7 +1579,7 @@ void V_DrawRightAlignedSmallString(INT32 x, INT32 y, INT32 option, const char *s // void V_DrawThinString(INT32 x, INT32 y, INT32 option, const char *string) { - INT32 w, c, cx = x, cy = y, dupx, dupy, scrwidth = BASEVIDWIDTH; + INT32 w, c, cx = x, cy = y, dupx, dupy, scrwidth, left = 0; const char *ch = string; INT32 charflags = 0; const UINT8 *colormap = NULL; @@ -1572,7 +1595,11 @@ void V_DrawThinString(INT32 x, INT32 y, INT32 option, const char *string) scrwidth = vid.width; } else + { dupx = dupy = 1; + scrwidth = vid.width/vid.dupx; + left = (scrwidth - BASEVIDWIDTH)/2; + } charflags = (option & V_CHARCOLORMASK); @@ -1628,9 +1655,9 @@ void V_DrawThinString(INT32 x, INT32 y, INT32 option, const char *string) else w = (SHORT(tny_font[c]->width) * dupx); - if (cx + w > scrwidth) + if (cx+left > scrwidth) break; - if (cx < 0) //left boundary check + if (cx+left + w < 0) //left boundary check { cx += w; continue; @@ -1653,7 +1680,7 @@ void V_DrawRightAlignedThinString(INT32 x, INT32 y, INT32 option, const char *st void V_DrawStringAtFixed(fixed_t x, fixed_t y, INT32 option, const char *string) { fixed_t cx = x, cy = y; - INT32 w, c, dupx, dupy, scrwidth = BASEVIDWIDTH, center = 0; + INT32 w, c, dupx, dupy, scrwidth, center = 0, left = 0; const char *ch = string; INT32 spacewidth = 4, charwidth = 0; @@ -1667,7 +1694,11 @@ void V_DrawStringAtFixed(fixed_t x, fixed_t y, INT32 option, const char *string) scrwidth = vid.width; } else + { dupx = dupy = 1; + scrwidth = vid.width/vid.dupx; + left = (scrwidth - BASEVIDWIDTH)/2; + } switch (option & V_SPACINGMASK) { @@ -1720,9 +1751,9 @@ void V_DrawStringAtFixed(fixed_t x, fixed_t y, INT32 option, const char *string) else w = SHORT(hu_font[c]->width) * dupx; - if ((cx>>FRACBITS) + w > scrwidth) + if ((cx>>FRACBITS)+left > scrwidth) break; - if (cx < 0) //left boundary check + if (cx+left + w < 0) //left boundary check { cx += w<