mirror of
https://git.do.srb2.org/STJr/SRB2.git
synced 2024-11-15 09:11:48 +00:00
Introducing Marathon Run. (I was going to call it Marathon Mode, but NiGHTS Mode being right next to it on the menu looked terrible.)
Basically a dedicated Record Attack-like experience for speedrunning the game as a continuous chunk rather than ILs. Has several quality of life features. Benefits include: * An unambiguous real-time bar across the bottom of the screen, always displaying the current time, ticking up until you reach the ending. * Disable the console (pausing is still allowed, but the timer will still increment). * Automatically skip intermissions as if you're holding down the spin button. * Show centiseconds on HUD automatically, like record attack. * "Live Event Backups" - a category of run fit for major events like GDQ, where recovery from crashes or chokes makes for better entertainment. Essentially a modified SP savefile, down to using the same basic functions, but has its own filename and tweaked internal layout. * "spmarathon_start" MainCfg block parameter and "marathonnext" mapheader parameter, allowing for a customised flow (makes this fit for purpose for an eventual SUGOI port). * Disabling inter-level custom cutscenes by default with a menu option to toggle this (won't show up if the mod doesn't *have* any custom cutscenes), although either way ending cutscenes (vanilla or custom) remain intact since is time is called before them. * Won't show up if you have a mod that consists of only one level (determined by spmarathon_start's nextlevel; this won't trip if you manually set its marathonnext). * Unconditional gratitude on the evaluation screen, instead of a negging "Try again..." if you didn't get all the emeralds (which you may not have been aiming for). * Gorgeous new menu (no new assets required, unless you wanna give it a header later). Changes which were required for the above but affect other areas of the game include: * "useBlackRock" MainCFG block parameter, which can be used to disable the presence of the Black Rock or Egg Rock in both the Evaluation screen and the Marathon Run menu (for total conversions with different stories). * Disabling Continues in NiGHTS mode, to match the most common singleplayer experience post 2.2.4's release (is reverted if useContinues is set to true). * Hiding the exitmove "powerup" outside of multiplayer. (Okay, this isn't really related, I just saw this bug in action a lot while doing test runs and got annoyed enough to fix it here.) * The ability to use V_DrawPromptBack (in hardcode only at the moment, but) to draw in terms of pixels rather than rows of text, by providing negative instead of positive inputs). * A refactoring of redundant game saves smattered across the ending, credits, and evaluation - in addition to saving the game slightly earlier. * Minor m_menu.c touchups and refactorings here and there. Built using feedback from the official server's #speedruns channel, among other places.
This commit is contained in:
parent
feced5ec3c
commit
d593e2e1bb
22 changed files with 692 additions and 96 deletions
|
@ -770,7 +770,7 @@ boolean CON_Responder(event_t *ev)
|
|||
// check for console toggle key
|
||||
if (ev->type != ev_console)
|
||||
{
|
||||
if (modeattacking || metalrecording)
|
||||
if (modeattacking || metalrecording || marathonmode)
|
||||
return false;
|
||||
|
||||
if (key == gamecontrol[gc_console][0] || key == gamecontrol[gc_console][1])
|
||||
|
|
|
@ -815,6 +815,7 @@ void D_StartTitle(void)
|
|||
// In case someone exits out at the same time they start a time attack run,
|
||||
// reset modeattacking
|
||||
modeattacking = ATTACKING_NONE;
|
||||
marathonmode = 0;
|
||||
|
||||
// empty maptol so mario/etc sounds don't play in sound test when they shouldn't
|
||||
maptol = 0;
|
||||
|
@ -1131,6 +1132,7 @@ void D_SRB2Main(void)
|
|||
|
||||
// default savegame
|
||||
strcpy(savegamename, SAVEGAMENAME"%u.ssg");
|
||||
strcpy(liveeventbackup,"liveevent.bkp"); // intentionally not ending with .ssg
|
||||
|
||||
{
|
||||
const char *userhome = D_Home(); //Alam: path to home
|
||||
|
@ -1159,6 +1161,7 @@ void D_SRB2Main(void)
|
|||
|
||||
// can't use sprintf since there is %u in savegamename
|
||||
strcatbf(savegamename, srb2home, PATHSEP);
|
||||
strcatbf(liveeventbackup, srb2home, PATHSEP);
|
||||
|
||||
snprintf(luafiledir, sizeof luafiledir, "%s" PATHSEP "luafiles", srb2home);
|
||||
#else // DEFAULTDIR
|
||||
|
@ -1171,6 +1174,7 @@ void D_SRB2Main(void)
|
|||
|
||||
// can't use sprintf since there is %u in savegamename
|
||||
strcatbf(savegamename, userhome, PATHSEP);
|
||||
strcatbf(liveeventbackup, userhome, PATHSEP);
|
||||
|
||||
snprintf(luafiledir, sizeof luafiledir, "%s" PATHSEP "luafiles", userhome);
|
||||
#endif // DEFAULTDIR
|
||||
|
|
|
@ -1578,6 +1578,22 @@ static void readlevelheader(MYFILE *f, INT32 num)
|
|||
|
||||
mapheaderinfo[num-1]->nextlevel = (INT16)i;
|
||||
}
|
||||
else if (fastcmp(word, "MARATHONNEXT"))
|
||||
{
|
||||
if (fastcmp(word2, "TITLE")) i = 1100;
|
||||
else if (fastcmp(word2, "EVALUATION")) i = 1101;
|
||||
else if (fastcmp(word2, "CREDITS")) i = 1102;
|
||||
else if (fastcmp(word2, "ENDING")) i = 1103;
|
||||
else
|
||||
// Support using the actual map name,
|
||||
// i.e., MarathonNext = AB, MarathonNext = FZ, etc.
|
||||
|
||||
// Convert to map number
|
||||
if (word2[0] >= 'A' && word2[0] <= 'Z' && word2[2] == '\0')
|
||||
i = M_MapNumber(word2[0], word2[1]);
|
||||
|
||||
mapheaderinfo[num-1]->marathonnext = (INT16)i;
|
||||
}
|
||||
else if (fastcmp(word, "TYPEOFLEVEL"))
|
||||
{
|
||||
if (i) // it's just a number
|
||||
|
@ -3920,7 +3936,20 @@ static void readmaincfg(MYFILE *f)
|
|||
else
|
||||
value = get_number(word2);
|
||||
|
||||
spstage_start = (INT16)value;
|
||||
spstage_start = spmarathon_start = (INT16)value;
|
||||
}
|
||||
else if (fastcmp(word, "SPMARATHON_START"))
|
||||
{
|
||||
// Support using the actual map name,
|
||||
// i.e., Level AB, Level FZ, etc.
|
||||
|
||||
// Convert to map number
|
||||
if (word2[0] >= 'A' && word2[0] <= 'Z')
|
||||
value = M_MapNumber(word2[0], word2[1]);
|
||||
else
|
||||
value = get_number(word2);
|
||||
|
||||
spmarathon_start = (INT16)value;
|
||||
}
|
||||
else if (fastcmp(word, "SSTAGE_START"))
|
||||
{
|
||||
|
@ -4014,6 +4043,17 @@ static void readmaincfg(MYFILE *f)
|
|||
introtoplay = 128;
|
||||
introchanged = true;
|
||||
}
|
||||
else if (fastcmp(word, "CREDITSCUTSCENE"))
|
||||
{
|
||||
creditscutscene = (UINT8)get_number(word2);
|
||||
// range check, you morons.
|
||||
if (creditscutscene > 128)
|
||||
creditscutscene = 128;
|
||||
}
|
||||
else if (fastcmp(word, "USEBLACKROCK"))
|
||||
{
|
||||
useBlackRock = (UINT8)(value || word2[0] == 'T' || word2[0] == 'Y');
|
||||
}
|
||||
else if (fastcmp(word, "LOOPTITLE"))
|
||||
{
|
||||
looptitle = (value || word2[0] == 'T' || word2[0] == 'Y');
|
||||
|
@ -4115,13 +4155,6 @@ static void readmaincfg(MYFILE *f)
|
|||
titlescrollyspeed = get_number(word2);
|
||||
titlechanged = true;
|
||||
}
|
||||
else if (fastcmp(word, "CREDITSCUTSCENE"))
|
||||
{
|
||||
creditscutscene = (UINT8)get_number(word2);
|
||||
// range check, you morons.
|
||||
if (creditscutscene > 128)
|
||||
creditscutscene = 128;
|
||||
}
|
||||
else if (fastcmp(word, "DISABLESPEEDADJUST"))
|
||||
{
|
||||
disableSpeedAdjust = (value || word2[0] == 'T' || word2[0] == 'Y');
|
||||
|
@ -9895,6 +9928,11 @@ struct {
|
|||
{"TC_BLINK",TC_BLINK},
|
||||
{"TC_DASHMODE",TC_DASHMODE},
|
||||
|
||||
// marathonmode flags
|
||||
//{"MA_INIT",MA_INIT}, -- should never see this
|
||||
{"MA_RUNNING",MA_RUNNING},
|
||||
{"MA_NOCUTSCENES",MA_NOCUTSCENES},
|
||||
|
||||
{NULL,0}
|
||||
};
|
||||
|
||||
|
|
|
@ -101,6 +101,9 @@ void I_FinishUpdate (void)
|
|||
if (cv_showping.value && netgame && consoleplayer != serverplayer)
|
||||
SCR_DisplayLocalPing();
|
||||
|
||||
if (marathonmode)
|
||||
SCR_DisplayMarathonInfo();
|
||||
|
||||
//blast it to the screen
|
||||
// this code sucks
|
||||
//memcpy(dascreen,screens[0],screenwidth*screenheight);
|
||||
|
|
|
@ -459,6 +459,7 @@ void CONS_Debug(INT32 debugflags, const char *fmt, ...) FUNCDEBUG;
|
|||
// Things that used to be in dstrings.h
|
||||
#define SAVEGAMENAME "srb2sav"
|
||||
char savegamename[256];
|
||||
char liveeventbackup[256];
|
||||
|
||||
// m_misc.h
|
||||
#ifdef GETTEXT
|
||||
|
|
|
@ -45,7 +45,18 @@ extern INT32 curWeather;
|
|||
extern INT32 cursaveslot;
|
||||
//extern INT16 lastmapsaved;
|
||||
extern INT16 lastmaploaded;
|
||||
extern boolean gamecomplete;
|
||||
extern UINT8 gamecomplete;
|
||||
|
||||
// Extra abilities/settings for skins (combinable stuff)
|
||||
typedef enum
|
||||
{
|
||||
MA_RUNNING = 1, // In action
|
||||
MA_INIT = 1<<1, // Initialisation
|
||||
MA_NOCUTSCENES = 1<<2 // No cutscenes
|
||||
} marathonmode_t;
|
||||
|
||||
extern marathonmode_t marathonmode;
|
||||
extern tic_t marathontime;
|
||||
|
||||
#define maxgameovers 13
|
||||
extern UINT8 numgameovers;
|
||||
|
@ -127,7 +138,7 @@ extern INT32 displayplayer;
|
|||
extern INT32 secondarydisplayplayer; // for splitscreen
|
||||
|
||||
// Maps of special importance
|
||||
extern INT16 spstage_start;
|
||||
extern INT16 spstage_start, spmarathon_start;
|
||||
extern INT16 sstage_start, sstage_end, smpstage_start, smpstage_end;
|
||||
|
||||
extern INT16 titlemap;
|
||||
|
@ -289,6 +300,7 @@ typedef struct
|
|||
UINT8 actnum; ///< Act number or 0 for none.
|
||||
UINT32 typeoflevel; ///< Combination of typeoflevel flags.
|
||||
INT16 nextlevel; ///< Map number of next level, or 1100-1102 to end.
|
||||
INT16 marathonnext; ///< See nextlevel, but for Marathon mode. Necessary to support hub worlds ala SUGOI.
|
||||
char keywords[33]; ///< Keywords separated by space to search for. 32 characters.
|
||||
char musname[7]; ///< Music track to play. "" for no music.
|
||||
UINT16 mustrack; ///< Subsong to play. Only really relevant for music modules and specific formats supported by GME. 0 to ignore.
|
||||
|
@ -575,11 +587,12 @@ extern UINT16 nightslinktics;
|
|||
|
||||
extern UINT8 introtoplay;
|
||||
extern UINT8 creditscutscene;
|
||||
extern UINT8 useBlackRock;
|
||||
|
||||
extern UINT8 use1upSound;
|
||||
extern UINT8 maxXtraLife; // Max extra lives from rings
|
||||
extern UINT8 useContinues;
|
||||
#define continuesInSession (!multiplayer && (useContinues || ultimatemode || !(cursaveslot > 0)))
|
||||
#define continuesInSession (!multiplayer && (ultimatemode || (useContinues && !marathonmode) || (!modeattacking && !(cursaveslot > 0))))
|
||||
|
||||
extern mobj_t *hunt1, *hunt2, *hunt3; // Emerald hunt locations
|
||||
|
||||
|
|
|
@ -1330,10 +1330,6 @@ void F_StartCredits(void)
|
|||
// Just in case they're open ... somehow
|
||||
M_ClearMenus(true);
|
||||
|
||||
// Save the second we enter the credits
|
||||
if ((!modifiedgame || savemoddata) && !(netgame || multiplayer) && cursaveslot > 0)
|
||||
G_SaveGame((UINT32)cursaveslot);
|
||||
|
||||
if (creditscutscene)
|
||||
{
|
||||
F_StartCustomCutscene(creditscutscene - 1, false, false);
|
||||
|
@ -1529,12 +1525,6 @@ void F_StartGameEvaluation(void)
|
|||
// Just in case they're open ... somehow
|
||||
M_ClearMenus(true);
|
||||
|
||||
// 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)
|
||||
G_SaveGame((UINT32)cursaveslot);
|
||||
|
||||
goodending = (ALL7EMERALDS(emeralds));
|
||||
|
||||
gameaction = ga_nothing;
|
||||
|
@ -1551,13 +1541,20 @@ void F_GameEvaluationDrawer(void)
|
|||
angle_t fa;
|
||||
INT32 eemeralds_cur;
|
||||
char patchname[7] = "CEMGx0";
|
||||
const char* endingtext = (goodending ? "CONGRATULATIONS!" : "TRY AGAIN...");
|
||||
const char* endingtext;
|
||||
|
||||
if (marathonmode)
|
||||
endingtext = "THANKS FOR THE RUN!";
|
||||
else if (goodending)
|
||||
endingtext = "CONGRATULATIONS!";
|
||||
else
|
||||
endingtext = "TRY AGAIN...";
|
||||
|
||||
V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31);
|
||||
|
||||
// Draw all the good crap here.
|
||||
|
||||
if (finalecount > 0)
|
||||
if (finalecount > 0 && useBlackRock)
|
||||
{
|
||||
INT32 scale = FRACUNIT;
|
||||
patch_t *rockpat;
|
||||
|
@ -1841,10 +1838,6 @@ void F_StartEnding(void)
|
|||
// Just in case they're open ... somehow
|
||||
M_ClearMenus(true);
|
||||
|
||||
// Save before the credits sequence.
|
||||
if ((!modifiedgame || savemoddata) && !(netgame || multiplayer) && cursaveslot > 0)
|
||||
G_SaveGame((UINT32)cursaveslot);
|
||||
|
||||
gameaction = ga_nothing;
|
||||
paused = false;
|
||||
CON_ToggleOff();
|
||||
|
@ -3959,6 +3952,7 @@ static void F_AdvanceToNextScene(void)
|
|||
animtimer = pictime = cutscenes[cutnum]->scene[scenenum].picduration[picnum];
|
||||
}
|
||||
|
||||
// See also G_AfterIntermission, the only other place which handles intra-map/ending transitions
|
||||
void F_EndCutScene(void)
|
||||
{
|
||||
cutsceneover = true; // do this first, just in case G_EndGame or something wants to turn it back false later
|
||||
|
|
88
src/g_game.c
88
src/g_game.c
|
@ -82,7 +82,10 @@ INT32 curWeather = PRECIP_NONE;
|
|||
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 gamecomplete = 0;
|
||||
|
||||
marathonmode_t marathonmode = 0;
|
||||
tic_t marathontime = 0;
|
||||
|
||||
UINT8 numgameovers = 0; // for startinglives balance
|
||||
SINT8 startinglivesbalance[maxgameovers+1] = {3, 5, 7, 9, 12, 15, 20, 25, 30, 40, 50, 75, 99, 0x7F};
|
||||
|
@ -118,7 +121,7 @@ UINT32 ssspheres; // old special stage
|
|||
INT16 lastmap; // last level you were at (returning from special stages)
|
||||
tic_t timeinmap; // Ticker for time spent in level (used for levelcard display)
|
||||
|
||||
INT16 spstage_start;
|
||||
INT16 spstage_start, spmarathon_start;
|
||||
INT16 sstage_start, sstage_end, smpstage_start, smpstage_end;
|
||||
|
||||
INT16 titlemap = 0;
|
||||
|
@ -223,6 +226,7 @@ UINT8 useContinues = 0; // Set to 1 to enable continues outside of no-save scena
|
|||
|
||||
UINT8 introtoplay;
|
||||
UINT8 creditscutscene;
|
||||
UINT8 useBlackRock = 1;
|
||||
|
||||
// Emerald locations
|
||||
mobj_t *hunt1;
|
||||
|
@ -769,6 +773,8 @@ void G_SetGameModified(boolean silent)
|
|||
// If in record attack recording, cancel it.
|
||||
if (modeattacking)
|
||||
M_EndModeAttackRun();
|
||||
else if (marathonmode)
|
||||
Command_ExitGame_f();
|
||||
}
|
||||
|
||||
/** Builds an original game map name from a map number.
|
||||
|
@ -3662,8 +3668,14 @@ static void G_DoCompleted(void)
|
|||
// nextmap is 0-based, unlike gamemap
|
||||
if (nextmapoverride != 0)
|
||||
nextmap = (INT16)(nextmapoverride-1);
|
||||
else if (marathonmode && mapheaderinfo[gamemap-1]->marathonnext)
|
||||
nextmap = (INT16)(mapheaderinfo[gamemap-1]->marathonnext-1);
|
||||
else
|
||||
{
|
||||
nextmap = (INT16)(mapheaderinfo[gamemap-1]->nextlevel-1);
|
||||
if (marathonmode && nextmap == spmarathon_start-1)
|
||||
nextmap = 1100-1; // No infinite loop for you
|
||||
}
|
||||
|
||||
// Remember last map for when you come out of the special stage.
|
||||
if (!spec)
|
||||
|
@ -3687,10 +3699,12 @@ static void G_DoCompleted(void)
|
|||
visitedmap[cm/8] |= (1<<(cm&7));
|
||||
if (!mapheaderinfo[cm])
|
||||
cm = -1; // guarantee error execution
|
||||
else if (marathonmode && mapheaderinfo[cm]->marathonnext)
|
||||
cm = (INT16)(mapheaderinfo[cm]->marathonnext-1);
|
||||
else
|
||||
cm = (INT16)(mapheaderinfo[cm]->nextlevel-1);
|
||||
|
||||
if (cm >= NUMMAPS || cm < 0) // out of range (either 1100-1102 or error)
|
||||
if (cm >= NUMMAPS || cm < 0) // out of range (either 1100ish or error)
|
||||
{
|
||||
cm = nextmap; //Start the loop again so that the error checking below is executed.
|
||||
|
||||
|
@ -3759,6 +3773,25 @@ static void G_DoCompleted(void)
|
|||
if (nextmap < NUMMAPS && !mapheaderinfo[nextmap])
|
||||
P_AllocMapHeader(nextmap);
|
||||
|
||||
// do this before going to the intermission or starting a custom cutscene, mostly for the sake of marathon mode but it also massively reduces redundant file save events in f_finale.c
|
||||
if (nextmap >= 1100-1)
|
||||
{
|
||||
if (!gamecomplete)
|
||||
gamecomplete = 2; // special temporary mode to prevent using SP level select in pause menu until the intermission is over without restricting it in every intermission
|
||||
if (cursaveslot > 0)
|
||||
{
|
||||
if (marathonmode)
|
||||
{
|
||||
// don't keep a backup around when the run is done!
|
||||
if (FIL_FileExists(liveeventbackup))
|
||||
remove(liveeventbackup);
|
||||
cursaveslot = 0;
|
||||
}
|
||||
else if ((!modifiedgame || savemoddata) && !(netgame || multiplayer))
|
||||
G_SaveGame((UINT32)cursaveslot);
|
||||
}
|
||||
}
|
||||
|
||||
if ((skipstats && !modeattacking) || (spec && modeattacking && stagefailed))
|
||||
{
|
||||
G_UpdateVisited();
|
||||
|
@ -3772,6 +3805,7 @@ static void G_DoCompleted(void)
|
|||
}
|
||||
}
|
||||
|
||||
// See also F_EndCutscene, the only other place which handles intra-map/ending transitions
|
||||
void G_AfterIntermission(void)
|
||||
{
|
||||
Y_CleanupScreenBuffer();
|
||||
|
@ -3782,9 +3816,12 @@ void G_AfterIntermission(void)
|
|||
return;
|
||||
}
|
||||
|
||||
if (gamecomplete == 2) // special temporary mode to prevent using SP level select in pause menu until the intermission is over without restricting it in every intermission
|
||||
gamecomplete = 1;
|
||||
|
||||
HU_ClearCEcho();
|
||||
|
||||
if ((gametyperules & GTR_CUTSCENES) && mapheaderinfo[gamemap-1]->cutscenenum && !modeattacking && skipstats <= 1) // Start a custom cutscene.
|
||||
if ((gametyperules & GTR_CUTSCENES) && mapheaderinfo[gamemap-1]->cutscenenum && !modeattacking && skipstats <= 1 && !(marathonmode & MA_NOCUTSCENES)) // Start a custom cutscene.
|
||||
F_StartCustomCutscene(mapheaderinfo[gamemap-1]->cutscenenum-1, false, false);
|
||||
else
|
||||
{
|
||||
|
@ -3925,7 +3962,7 @@ void G_EndGame(void)
|
|||
void G_LoadGameSettings(void)
|
||||
{
|
||||
// defaults
|
||||
spstage_start = 1;
|
||||
spstage_start = spmarathon_start = 1;
|
||||
sstage_start = 50;
|
||||
sstage_end = 56; // 7 special stages in vanilla SRB2
|
||||
sstage_end++; // plus one weirdo
|
||||
|
@ -4245,7 +4282,10 @@ void G_LoadGame(UINT32 slot, INT16 mapoverride)
|
|||
startonmapnum = mapoverride;
|
||||
#endif
|
||||
|
||||
sprintf(savename, savegamename, slot);
|
||||
if (marathonmode)
|
||||
strcpy(savename, liveeventbackup);
|
||||
else
|
||||
sprintf(savename, savegamename, slot);
|
||||
|
||||
length = FIL_ReadFile(savename, &savebuffer);
|
||||
if (!length)
|
||||
|
@ -4257,7 +4297,7 @@ void G_LoadGame(UINT32 slot, INT16 mapoverride)
|
|||
save_p = savebuffer;
|
||||
|
||||
memset(vcheck, 0, sizeof (vcheck));
|
||||
sprintf(vcheck, "version %d", VERSION);
|
||||
sprintf(vcheck, (marathonmode ? "back-up %d" : "version %d"), VERSION);
|
||||
if (strcmp((const char *)save_p, (const char *)vcheck))
|
||||
{
|
||||
#ifdef SAVEGAME_OTHERVERSIONS
|
||||
|
@ -4297,6 +4337,11 @@ void G_LoadGame(UINT32 slot, INT16 mapoverride)
|
|||
memset(&savedata, 0, sizeof(savedata));
|
||||
return;
|
||||
}
|
||||
if (marathonmode)
|
||||
{
|
||||
marathontime = READUINT32(save_p);
|
||||
marathonmode |= READUINT8(save_p);
|
||||
}
|
||||
|
||||
// done
|
||||
Z_Free(savebuffer);
|
||||
|
@ -4325,13 +4370,12 @@ void G_SaveGame(UINT32 slot)
|
|||
char savename[256] = "";
|
||||
const char *backup;
|
||||
|
||||
sprintf(savename, savegamename, slot);
|
||||
if (marathonmode)
|
||||
strcpy(savename, liveeventbackup);
|
||||
else
|
||||
sprintf(savename, savegamename, slot);
|
||||
backup = va("%s",savename);
|
||||
|
||||
// save during evaluation or credits? game's over, folks!
|
||||
if (gamestate == GS_ENDING || gamestate == GS_CREDITS || gamestate == GS_EVALUATION)
|
||||
gamecomplete = true;
|
||||
|
||||
gameaction = ga_nothing;
|
||||
{
|
||||
char name[VERSIONSIZE];
|
||||
|
@ -4345,10 +4389,15 @@ void G_SaveGame(UINT32 slot)
|
|||
}
|
||||
|
||||
memset(name, 0, sizeof (name));
|
||||
sprintf(name, "version %d", VERSION);
|
||||
sprintf(name, (marathonmode ? "back-up %d" : "version %d"), VERSION);
|
||||
WRITEMEM(save_p, name, VERSIONSIZE);
|
||||
|
||||
P_SaveGame();
|
||||
if (marathonmode)
|
||||
{
|
||||
WRITEUINT32(save_p, marathontime);
|
||||
WRITEUINT8(save_p, (marathonmode & ~MA_INIT));
|
||||
}
|
||||
|
||||
length = save_p - savebuffer;
|
||||
saved = FIL_WriteFile(backup, savebuffer, length);
|
||||
|
@ -4361,7 +4410,7 @@ void G_SaveGame(UINT32 slot)
|
|||
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);
|
||||
CONS_Alert(CONS_ERROR, M_GetText("Error while writing to %s for save slot %u, base: %s\n"), backup, slot, (marathonmode ? liveeventbackup : savegamename));
|
||||
}
|
||||
|
||||
#define BADSAVE goto cleanup;
|
||||
|
@ -4374,7 +4423,10 @@ void G_SaveGameOver(UINT32 slot, boolean modifylives)
|
|||
char savename[255];
|
||||
const char *backup;
|
||||
|
||||
sprintf(savename, savegamename, slot);
|
||||
if (marathonmode)
|
||||
strcpy(savename, liveeventbackup);
|
||||
else
|
||||
sprintf(savename, savegamename, slot);
|
||||
backup = va("%s",savename);
|
||||
|
||||
length = FIL_ReadFile(savename, &savebuffer);
|
||||
|
@ -4393,7 +4445,7 @@ void G_SaveGameOver(UINT32 slot, boolean modifylives)
|
|||
save_p = savebuffer;
|
||||
// Version check
|
||||
memset(vcheck, 0, sizeof (vcheck));
|
||||
sprintf(vcheck, "version %d", VERSION);
|
||||
sprintf(vcheck, (marathonmode ? "back-up %d" : "version %d"), VERSION);
|
||||
if (strcmp((const char *)save_p, (const char *)vcheck)) BADSAVE
|
||||
save_p += VERSIONSIZE;
|
||||
|
||||
|
@ -4460,7 +4512,7 @@ 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);
|
||||
CONS_Alert(CONS_ERROR, M_GetText("Error while writing to %s for save slot %u, base: %s\n"), backup, slot, (marathonmode ? liveeventbackup : savegamename));
|
||||
Z_Free(savebuffer);
|
||||
save_p = savebuffer = NULL;
|
||||
|
||||
|
@ -4598,7 +4650,7 @@ void G_InitNew(UINT8 pultmode, const char *mapname, boolean resetplayer, boolean
|
|||
automapactive = false;
|
||||
imcontinuing = false;
|
||||
|
||||
if ((gametyperules & GTR_CUTSCENES) && !skipprecutscene && mapheaderinfo[gamemap-1]->precutscenenum && !modeattacking) // Start a custom cutscene.
|
||||
if ((gametyperules & GTR_CUTSCENES) && !skipprecutscene && mapheaderinfo[gamemap-1]->precutscenenum && !modeattacking && !(marathonmode & MA_NOCUTSCENES)) // Start a custom cutscene.
|
||||
F_StartCustomCutscene(mapheaderinfo[gamemap-1]->precutscenenum-1, true, resetplayer);
|
||||
else
|
||||
G_DoLoadLevel(resetplayer);
|
||||
|
|
|
@ -904,7 +904,11 @@ void HWR_DrawTutorialBack(UINT32 color, INT32 boxheight)
|
|||
{
|
||||
FOutVector v[4];
|
||||
FSurfaceInfo Surf;
|
||||
INT32 height = (boxheight * 4) + (boxheight/2)*5; // 4 lines of space plus gaps between and some leeway
|
||||
INT32 height;
|
||||
if (boxheight < 0)
|
||||
height = -boxheight;
|
||||
else
|
||||
height = (boxheight * 4) + (boxheight/2)*5; // 4 lines of space plus gaps between and some leeway
|
||||
|
||||
// setup some neat-o translucency effect
|
||||
|
||||
|
|
|
@ -2009,6 +2009,8 @@ static int mapheaderinfo_get(lua_State *L)
|
|||
lua_pushinteger(L, header->typeoflevel);
|
||||
else if (fastcmp(field,"nextlevel"))
|
||||
lua_pushinteger(L, header->nextlevel);
|
||||
else if (fastcmp(field,"marathonnext"))
|
||||
lua_pushinteger(L, header->marathonnext);
|
||||
else if (fastcmp(field,"keywords"))
|
||||
lua_pushstring(L, header->keywords);
|
||||
else if (fastcmp(field,"musname"))
|
||||
|
|
|
@ -115,7 +115,10 @@ int LUA_PushGlobals(lua_State *L, const char *word)
|
|||
lua_pushboolean(L, splitscreen);
|
||||
return 1;
|
||||
} else if (fastcmp(word,"gamecomplete")) {
|
||||
lua_pushboolean(L, gamecomplete);
|
||||
lua_pushboolean(L, (gamecomplete != 0));
|
||||
return 1;
|
||||
} else if (fastcmp(word,"marathonmode")) {
|
||||
lua_pushinteger(L, marathonmode);
|
||||
return 1;
|
||||
} else if (fastcmp(word,"devparm")) {
|
||||
lua_pushboolean(L, devparm);
|
||||
|
@ -145,6 +148,9 @@ int LUA_PushGlobals(lua_State *L, const char *word)
|
|||
} else if (fastcmp(word,"spstage_start")) {
|
||||
lua_pushinteger(L, spstage_start);
|
||||
return 1;
|
||||
} else if (fastcmp(word,"spmarathon_start")) {
|
||||
lua_pushinteger(L, spmarathon_start);
|
||||
return 1;
|
||||
} else if (fastcmp(word,"sstage_start")) {
|
||||
lua_pushinteger(L, sstage_start);
|
||||
return 1;
|
||||
|
|
502
src/m_menu.c
502
src/m_menu.c
|
@ -182,6 +182,7 @@ static tic_t keydown = 0;
|
|||
|
||||
static void M_GoBack(INT32 choice);
|
||||
static void M_StopMessage(INT32 choice);
|
||||
static boolean stopstopmessage = false;
|
||||
|
||||
#ifndef NONET
|
||||
static void M_HandleServerPage(INT32 choice);
|
||||
|
@ -252,6 +253,7 @@ static void M_ConfirmTeamScramble(INT32 choice);
|
|||
static void M_ConfirmTeamChange(INT32 choice);
|
||||
static void M_SecretsMenu(INT32 choice);
|
||||
static void M_SetupChoosePlayer(INT32 choice);
|
||||
static UINT8 M_SetupChoosePlayerDirect(INT32 choice);
|
||||
static void M_QuitSRB2(INT32 choice);
|
||||
menu_t SP_MainDef, OP_MainDef;
|
||||
menu_t MISC_ScrambleTeamDef, MISC_ChangeTeamDef;
|
||||
|
@ -272,9 +274,14 @@ static void M_ModeAttackEndGame(INT32 choice);
|
|||
static void M_SetGuestReplay(INT32 choice);
|
||||
static void M_HandleChoosePlayerMenu(INT32 choice);
|
||||
static void M_ChoosePlayer(INT32 choice);
|
||||
static void M_MarathonLiveEventBackup(INT32 choice);
|
||||
static void M_Marathon(INT32 choice);
|
||||
static void M_HandleMarathonChoosePlayer(INT32 choice);
|
||||
static void M_StartMarathon(INT32 choice);
|
||||
menu_t SP_LevelStatsDef;
|
||||
static menu_t SP_TimeAttackDef, SP_ReplayDef, SP_GuestReplayDef, SP_GhostDef;
|
||||
static menu_t SP_NightsAttackDef, SP_NightsReplayDef, SP_NightsGuestReplayDef, SP_NightsGhostDef;
|
||||
static menu_t SP_MarathonDef;
|
||||
|
||||
// Multiplayer
|
||||
static void M_SetupMultiPlayer(INT32 choice);
|
||||
|
@ -354,6 +361,7 @@ static void M_DrawLoad(void);
|
|||
static void M_DrawLevelStats(void);
|
||||
static void M_DrawTimeAttackMenu(void);
|
||||
static void M_DrawNightsAttackMenu(void);
|
||||
static void M_DrawMarathon(void);
|
||||
static void M_DrawSetupChoosePlayerMenu(void);
|
||||
static void M_DrawControlsDefMenu(void);
|
||||
static void M_DrawCameraOptionsMenu(void);
|
||||
|
@ -476,6 +484,11 @@ static consvar_t cv_dummylives = {"dummylives", "0", CV_HIDEN, liveslimit_cons_t
|
|||
static consvar_t cv_dummycontinues = {"dummycontinues", "0", CV_HIDEN, contlimit_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
|
||||
static consvar_t cv_dummymares = {"dummymares", "Overall", CV_HIDEN|CV_CALL, dummymares_cons_t, Dummymares_OnChange, 0, NULL, NULL, 0, 0, NULL};
|
||||
|
||||
CV_PossibleValue_t marathon_cons_t[] = {{0, "Standard"}, {1, "Live Event Backup"}, {2, "Ultimate"}, {0, NULL}};
|
||||
|
||||
consvar_t cv_dummymarathon = {"dummymarathon", "Standard", CV_HIDEN, marathon_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
|
||||
consvar_t cv_dummycutscenes = {"dummycutscenes", "Off", CV_HIDEN, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
|
||||
|
||||
// ==========================================================================
|
||||
// ORGANIZATION START.
|
||||
// ==========================================================================
|
||||
|
@ -746,10 +759,11 @@ static menuitem_t SR_EmblemHintMenu[] =
|
|||
// Single Player Main
|
||||
static menuitem_t SP_MainMenu[] =
|
||||
{
|
||||
{IT_CALL | IT_STRING, NULL, "Start Game", M_LoadGame, 84},
|
||||
{IT_SECRET, NULL, "Record Attack", M_TimeAttack, 92},
|
||||
{IT_SECRET, NULL, "NiGHTS Mode", M_NightsAttack, 100},
|
||||
{IT_CALL | IT_STRING, NULL, "Tutorial", M_StartTutorial, 108},
|
||||
{IT_CALL | IT_STRING, NULL, "Start Game", M_LoadGame, 76},
|
||||
{IT_SECRET, NULL, "Record Attack", M_TimeAttack, 84},
|
||||
{IT_SECRET, NULL, "NiGHTS Mode", M_NightsAttack, 92},
|
||||
{IT_CALL | IT_STRING, NULL, "Tutorial", M_StartTutorial, 100},
|
||||
{IT_CALL | IT_STRING | IT_CALL_NOTMODIFIED, NULL, "Marathon Run", M_Marathon, 108},
|
||||
{IT_CALL | IT_STRING | IT_CALL_NOTMODIFIED, NULL, "Statistics", M_Statistics, 116}
|
||||
};
|
||||
|
||||
|
@ -759,6 +773,7 @@ enum
|
|||
sprecordattack,
|
||||
spnightsmode,
|
||||
sptutorial,
|
||||
spmarathon,
|
||||
spstatistics
|
||||
};
|
||||
|
||||
|
@ -901,6 +916,23 @@ enum
|
|||
nastart
|
||||
};
|
||||
|
||||
// Marathon
|
||||
static menuitem_t SP_MarathonMenu[] =
|
||||
{
|
||||
{IT_STRING|IT_KEYHANDLER, NULL, "Character", M_HandleMarathonChoosePlayer, 100},
|
||||
{IT_STRING|IT_CVAR, NULL, "Category", &cv_dummymarathon, 110},
|
||||
{IT_STRING|IT_CVAR, NULL, "Cutscenes", &cv_dummycutscenes, 120},
|
||||
{IT_WHITESTRING|IT_CALL, NULL, "Start", M_StartMarathon, 130},
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
marathonplayer,
|
||||
marathonultimate,
|
||||
marathoncutscenes,
|
||||
marathonstart
|
||||
};
|
||||
|
||||
// Statistics
|
||||
static menuitem_t SP_LevelStatsMenu[] =
|
||||
{
|
||||
|
@ -1908,6 +1940,18 @@ static menu_t SP_NightsGhostDef =
|
|||
NULL
|
||||
};
|
||||
|
||||
static menu_t SP_MarathonDef =
|
||||
{
|
||||
MTREE2(MN_SP_MAIN, MN_SP_MARATHON),
|
||||
"M_ATTACK", // temporary
|
||||
sizeof(SP_MarathonMenu)/sizeof(menuitem_t),
|
||||
&MainDef, // Doesn't matter.
|
||||
SP_MarathonMenu,
|
||||
M_DrawMarathon,
|
||||
32, 40,
|
||||
0,
|
||||
NULL
|
||||
};
|
||||
|
||||
menu_t SP_PlayerDef =
|
||||
{
|
||||
|
@ -2524,6 +2568,8 @@ void M_InitMenuPresTables(void)
|
|||
strncpy(menupres[i].musname, "_recat", 7);
|
||||
else if (i == MN_SP_NIGHTSATTACK)
|
||||
strncpy(menupres[i].musname, "_nitat", 7);
|
||||
else if (i == MN_SP_MARATHON)
|
||||
strncpy(menupres[i].musname, "spec8", 6);
|
||||
else if (i == MN_SP_PLAYER || i == MN_SR_PLAYER)
|
||||
strncpy(menupres[i].musname, "_chsel", 7);
|
||||
else if (i == MN_SR_SOUNDTEST)
|
||||
|
@ -3018,7 +3064,7 @@ static void M_GoBack(INT32 choice)
|
|||
netgame = multiplayer = false;
|
||||
}
|
||||
|
||||
if ((currentMenu->prevMenu == &MainDef) && (currentMenu == &SP_TimeAttackDef || currentMenu == &SP_NightsAttackDef))
|
||||
if ((currentMenu->prevMenu == &MainDef) && (currentMenu == &SP_TimeAttackDef || currentMenu == &SP_NightsAttackDef || currentMenu == &SP_MarathonDef))
|
||||
{
|
||||
// D_StartTitle does its own wipe, since GS_TIMEATTACK is now a complete gamestate.
|
||||
|
||||
|
@ -3403,11 +3449,14 @@ boolean M_Responder(event_t *ev)
|
|||
{
|
||||
if (currentMenu->menuitems[itemOn].alphaKey != MM_EVENTHANDLER)
|
||||
{
|
||||
if (ch == ' ' || ch == 'n' || ch == 'y' || ch == KEY_ESCAPE || ch == KEY_ENTER)
|
||||
if (ch == ' ' || ch == 'n' || ch == 'y' || ch == KEY_ESCAPE || ch == KEY_ENTER || ch == KEY_DEL)
|
||||
{
|
||||
if (routine)
|
||||
routine(ch);
|
||||
M_StopMessage(0);
|
||||
if (stopstopmessage)
|
||||
stopstopmessage = false;
|
||||
else
|
||||
M_StopMessage(0);
|
||||
noFurtherInput = true;
|
||||
return true;
|
||||
}
|
||||
|
@ -3657,7 +3706,7 @@ void M_StartControlPanel(void)
|
|||
{
|
||||
INT32 numlives = 2;
|
||||
|
||||
SPauseMenu[spause_pandora].status = (M_SecretUnlocked(SECRET_PANDORA)) ? (IT_STRING | IT_CALL) : (IT_DISABLED);
|
||||
SPauseMenu[spause_pandora].status = (M_SecretUnlocked(SECRET_PANDORA) && !marathonmode) ? (IT_STRING | IT_CALL) : (IT_DISABLED);
|
||||
|
||||
if (&players[consoleplayer])
|
||||
{
|
||||
|
@ -3675,7 +3724,7 @@ void M_StartControlPanel(void)
|
|||
}
|
||||
|
||||
// We can always use level select though. :33
|
||||
SPauseMenu[spause_levelselect].status = (gamecomplete) ? (IT_STRING | IT_CALL) : (IT_DISABLED);
|
||||
SPauseMenu[spause_levelselect].status = (gamecomplete == 1) ? (IT_STRING | IT_CALL) : (IT_DISABLED);
|
||||
|
||||
// And emblem hints.
|
||||
SPauseMenu[spause_hints].status = (M_SecretUnlocked(SECRET_EMBLEMHINTS)) ? (IT_STRING | IT_CALL) : (IT_DISABLED);
|
||||
|
@ -3859,6 +3908,8 @@ void M_Init(void)
|
|||
CV_RegisterVar(&cv_dummylives);
|
||||
CV_RegisterVar(&cv_dummycontinues);
|
||||
CV_RegisterVar(&cv_dummymares);
|
||||
CV_RegisterVar(&cv_dummymarathon);
|
||||
CV_RegisterVar(&cv_dummycutscenes);
|
||||
|
||||
quitmsg[QUITMSG] = M_GetText("Eggman's tied explosives\nto your girlfriend, and\nwill activate them if\nyou press the 'Y' key!\nPress 'N' to save her!\n\n(Press 'Y' to quit)");
|
||||
quitmsg[QUITMSG1] = M_GetText("What would Tails say if\nhe saw you quitting the game?\n\n(Press 'Y' to quit)");
|
||||
|
@ -6321,8 +6372,8 @@ static char *M_AddonsHeaderPath(void)
|
|||
|
||||
static void M_AddonsClearName(INT32 choice)
|
||||
{
|
||||
(void)choice;
|
||||
CLEARNAME;
|
||||
M_StopMessage(choice);
|
||||
}
|
||||
|
||||
// returns whether to do message draw
|
||||
|
@ -6354,7 +6405,7 @@ static boolean M_AddonsRefresh(void)
|
|||
|
||||
if (message)
|
||||
{
|
||||
M_StartMessage(message,M_AddonsClearName,MM_EVENTHANDLER);
|
||||
M_StartMessage(message,M_AddonsClearName,MM_NOTHING);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -8011,6 +8062,15 @@ static void M_SinglePlayerMenu(INT32 choice)
|
|||
|
||||
SP_MainMenu[sptutorial].status = tutorialmap ? IT_CALL|IT_STRING : IT_NOTHING|IT_DISABLED;
|
||||
|
||||
// If the FIRST stage immediately leads to the ending, or itself (which gets converted to the title screen in G_DoCompleted for marathonmode only), there's no point in having this option on the menu. You should use Record Attack in that circumstance, although if marathonnext is set this behaviour can be overridden if you make some weird mod that requires multiple playthroughs of the same map in sequence and has some in-level mechanism to break the cycle.
|
||||
if (mapheaderinfo[spmarathon_start-1]
|
||||
&& !mapheaderinfo[spmarathon_start-1]->marathonnext
|
||||
&& (mapheaderinfo[spmarathon_start-1]->nextlevel == spmarathon_start
|
||||
|| mapheaderinfo[spmarathon_start-1]->nextlevel >= 1100))
|
||||
SP_MainMenu[spmarathon].status = IT_NOTHING|IT_DISABLED;
|
||||
else
|
||||
SP_MainMenu[spmarathon].status = IT_CALL|IT_STRING|IT_CALL_NOTMODIFIED;
|
||||
|
||||
M_SetupNextMenu(&SP_MainDef);
|
||||
}
|
||||
|
||||
|
@ -8101,7 +8161,7 @@ static void M_StartTutorial(INT32 choice)
|
|||
emeralds = 0;
|
||||
memset(&luabanks, 0, sizeof(luabanks));
|
||||
M_ClearMenus(true);
|
||||
gamecomplete = false;
|
||||
gamecomplete = 0;
|
||||
cursaveslot = 0;
|
||||
G_DeferedInitNew(false, G_BuildMapName(tutorialmap), 0, false, false);
|
||||
}
|
||||
|
@ -8864,7 +8924,7 @@ static void M_CacheCharacterSelect(void)
|
|||
{
|
||||
INT32 i, skinnum;
|
||||
|
||||
for (i = 0; i < 32; i++)
|
||||
for (i = 0; i < MAXSKINS; i++)
|
||||
{
|
||||
if (!description[i].used)
|
||||
continue;
|
||||
|
@ -8876,7 +8936,7 @@ static void M_CacheCharacterSelect(void)
|
|||
}
|
||||
}
|
||||
|
||||
static void M_SetupChoosePlayer(INT32 choice)
|
||||
static UINT8 M_SetupChoosePlayerDirect(INT32 choice)
|
||||
{
|
||||
INT32 skinnum;
|
||||
UINT8 i;
|
||||
|
@ -8887,7 +8947,7 @@ static void M_SetupChoosePlayer(INT32 choice)
|
|||
|
||||
if (!mapheaderinfo[startmap-1] || mapheaderinfo[startmap-1]->forcecharacter[0] == '\0')
|
||||
{
|
||||
for (i = 0; i < 32; i++) // Handle charsels, availability, and unlocks.
|
||||
for (i = 0; i < MAXSKINS; i++) // Handle charsels, availability, and unlocks.
|
||||
{
|
||||
if (description[i].used) // If the character's disabled through SOC, there's nothing we can do for it.
|
||||
{
|
||||
|
@ -8895,7 +8955,7 @@ static void M_SetupChoosePlayer(INT32 choice)
|
|||
if (and)
|
||||
{
|
||||
char firstskin[SKINNAMESIZE+1];
|
||||
if (mapheaderinfo[startmap-1]->typeoflevel & TOL_NIGHTS) // skip tagteam characters for NiGHTS levels
|
||||
if (mapheaderinfo[startmap-1] && mapheaderinfo[startmap-1]->typeoflevel & TOL_NIGHTS) // skip tagteam characters for NiGHTS levels
|
||||
continue;
|
||||
strncpy(firstskin, description[i].skinname, (and - description[i].skinname));
|
||||
firstskin[(and - description[i].skinname)] = '\0';
|
||||
|
@ -8932,14 +8992,36 @@ static void M_SetupChoosePlayer(INT32 choice)
|
|||
|
||||
if (firstvalid == lastvalid) // We're being forced into a specific character, so might as well just skip it.
|
||||
{
|
||||
M_ChoosePlayer(firstvalid);
|
||||
return;
|
||||
return firstvalid;
|
||||
}
|
||||
|
||||
// One last bit of order we can't do in the iteration above.
|
||||
description[firstvalid].prev = lastvalid;
|
||||
description[lastvalid].next = firstvalid;
|
||||
|
||||
if (!allowed)
|
||||
{
|
||||
char_on = firstvalid;
|
||||
if (startchar > 0 && startchar < MAXSKINS)
|
||||
{
|
||||
INT16 workchar = startchar;
|
||||
while (workchar--)
|
||||
char_on = description[char_on].next;
|
||||
}
|
||||
}
|
||||
|
||||
return MAXSKINS;
|
||||
}
|
||||
|
||||
static void M_SetupChoosePlayer(INT32 choice)
|
||||
{
|
||||
UINT8 skinset = M_SetupChoosePlayerDirect(choice);
|
||||
if (skinset != MAXSKINS)
|
||||
{
|
||||
M_ChoosePlayer(skinset);
|
||||
return;
|
||||
}
|
||||
|
||||
M_ChangeMenuMusic("_chsel", true);
|
||||
|
||||
/* the menus suck -James */
|
||||
|
@ -8954,16 +9036,6 @@ static void M_SetupChoosePlayer(INT32 choice)
|
|||
|
||||
SP_PlayerDef.prevMenu = currentMenu;
|
||||
M_SetupNextMenu(&SP_PlayerDef);
|
||||
if (!allowed)
|
||||
{
|
||||
char_on = firstvalid;
|
||||
if (startchar > 0 && startchar < 32)
|
||||
{
|
||||
INT16 workchar = startchar;
|
||||
while (workchar--)
|
||||
char_on = description[char_on].next;
|
||||
}
|
||||
}
|
||||
|
||||
// finish scrolling the menu
|
||||
char_scroll = 0;
|
||||
|
@ -9022,6 +9094,10 @@ static void M_HandleChoosePlayerMenu(INT32 choice)
|
|||
|
||||
case KEY_ENTER:
|
||||
S_StartSound(NULL, sfx_menu1);
|
||||
char_scroll = 0; // finish scrolling the menu
|
||||
M_DrawSetupChoosePlayerMenu(); // draw the finally selected character one last time for the fadeout
|
||||
// Is this a hack?
|
||||
charseltimer = 0;
|
||||
M_ChoosePlayer(char_on);
|
||||
break;
|
||||
|
||||
|
@ -9261,7 +9337,7 @@ static void M_DrawSetupChoosePlayerMenu(void)
|
|||
// Chose the player you want to use Tails 03-02-2002
|
||||
static void M_ChoosePlayer(INT32 choice)
|
||||
{
|
||||
boolean ultmode = (ultimate_selectable && SP_PlayerDef.prevMenu == &SP_LoadDef && saveSlotSelected == NOSAVESLOT);
|
||||
boolean ultmode = (currentMenu == &SP_MarathonDef) ? (cv_dummymarathon.value == 2) : (ultimate_selectable && SP_PlayerDef.prevMenu == &SP_LoadDef && saveSlotSelected == NOSAVESLOT);
|
||||
UINT8 skinnum;
|
||||
|
||||
// skip this if forcecharacter or no characters available
|
||||
|
@ -9273,11 +9349,6 @@ static void M_ChoosePlayer(INT32 choice)
|
|||
// M_SetupChoosePlayer didn't call us directly, that means we've been properly set up.
|
||||
else
|
||||
{
|
||||
char_scroll = 0; // finish scrolling the menu
|
||||
M_DrawSetupChoosePlayerMenu(); // draw the finally selected character one last time for the fadeout
|
||||
// Is this a hack?
|
||||
charseltimer = 0;
|
||||
|
||||
skinnum = description[choice].skinnum[0];
|
||||
|
||||
if ((botingame = (description[choice].skinnum[1] != -1))) {
|
||||
|
@ -9291,11 +9362,11 @@ static void M_ChoosePlayer(INT32 choice)
|
|||
|
||||
M_ClearMenus(true);
|
||||
|
||||
if (startmap != spstage_start)
|
||||
if (!marathonmode && startmap != spstage_start)
|
||||
cursaveslot = 0;
|
||||
|
||||
//lastmapsaved = 0;
|
||||
gamecomplete = false;
|
||||
gamecomplete = 0;
|
||||
|
||||
G_DeferedInitNew(ultmode, G_BuildMapName(startmap), skinnum, false, fromlevelselect);
|
||||
COM_BufAddText("dummyconsvar 1\n"); // G_DeferedInitNew doesn't do this
|
||||
|
@ -10294,6 +10365,364 @@ static void M_ModeAttackEndGame(INT32 choice)
|
|||
Nextmap_OnChange();
|
||||
}
|
||||
|
||||
static void M_MarathonLiveEventBackup(INT32 choice)
|
||||
{
|
||||
if (choice == 'y' || choice == KEY_ENTER)
|
||||
{
|
||||
marathonmode = MA_INIT;
|
||||
G_LoadGame(MARATHONSLOT, 0);
|
||||
cursaveslot = MARATHONSLOT;
|
||||
if (!(marathonmode & MA_RUNNING))
|
||||
marathonmode = 0;
|
||||
return;
|
||||
}
|
||||
else if (choice == KEY_DEL)
|
||||
{
|
||||
M_StopMessage(0);
|
||||
if (FIL_FileExists(liveeventbackup)) // just in case someone deleted it while we weren't looking.
|
||||
remove(liveeventbackup);
|
||||
BwehHehHe();
|
||||
M_StartMessage("Live event backup erased.\n",M_Marathon,MM_NOTHING);
|
||||
stopstopmessage = true;
|
||||
return;
|
||||
}
|
||||
|
||||
M_Marathon(-1);
|
||||
}
|
||||
|
||||
// Going to Marathon menu...
|
||||
static void M_Marathon(INT32 choice)
|
||||
{
|
||||
UINT8 skinset;
|
||||
INT32 mapnum = 0;
|
||||
|
||||
if (choice != -1 && FIL_FileExists(liveeventbackup))
|
||||
{
|
||||
M_StartMessage(\
|
||||
"\x82Live event backup detected.\n\x80\
|
||||
Do you want to resurrect the last run?\n\
|
||||
(Fs in chat if we crashed on stream.)\n\
|
||||
\n\
|
||||
Press 'Y' or 'Enter' to resume,\n\
|
||||
'Del' to delete, or any other\n\
|
||||
key to continue to Marathon Run.",M_MarathonLiveEventBackup,MM_YESNO);
|
||||
return;
|
||||
}
|
||||
|
||||
fromlevelselect = false;
|
||||
|
||||
startmap = spmarathon_start;
|
||||
CV_SetValue(&cv_newgametype, GT_COOP); // Graue 09-08-2004
|
||||
|
||||
skinset = M_SetupChoosePlayerDirect(-1);
|
||||
|
||||
SP_MarathonMenu[marathonplayer].status = (skinset == MAXSKINS) ? IT_KEYHANDLER|IT_STRING : IT_NOTHING|IT_DISABLED;
|
||||
|
||||
while (mapnum < NUMMAPS)
|
||||
{
|
||||
if (mapheaderinfo[mapnum])
|
||||
{
|
||||
if (mapheaderinfo[mapnum]->cutscenenum || mapheaderinfo[mapnum]->precutscenenum)
|
||||
break;
|
||||
}
|
||||
mapnum++;
|
||||
}
|
||||
|
||||
SP_MarathonMenu[marathoncutscenes].status = (mapnum < NUMMAPS) ? IT_CVAR |IT_STRING : IT_NOTHING|IT_DISABLED;
|
||||
|
||||
M_ChangeMenuMusic("spec8", true);
|
||||
|
||||
SP_MarathonDef.prevMenu = &MainDef;
|
||||
G_SetGamestate(GS_TIMEATTACK); // do this before M_SetupNextMenu so that menu meta state knows that we're switching
|
||||
titlemapinaction = TITLEMAP_OFF; // Nope don't give us HOMs please
|
||||
M_SetupNextMenu(&SP_MarathonDef);
|
||||
itemOn = marathonstart; // "Start" is selected.
|
||||
recatkdrawtimer = 50-8;
|
||||
char_scroll = 0;
|
||||
}
|
||||
|
||||
static void M_HandleMarathonChoosePlayer(INT32 choice)
|
||||
{
|
||||
INT32 selectval;
|
||||
|
||||
if (keydown > 1)
|
||||
return;
|
||||
|
||||
switch (choice)
|
||||
{
|
||||
case KEY_DOWNARROW:
|
||||
M_NextOpt();
|
||||
break;
|
||||
case KEY_UPARROW:
|
||||
M_PrevOpt();
|
||||
break;
|
||||
|
||||
case KEY_LEFTARROW:
|
||||
if ((selectval = description[char_on].prev) == char_on)
|
||||
return;
|
||||
char_on = selectval;
|
||||
break;
|
||||
case KEY_RIGHTARROW:
|
||||
if ((selectval = description[char_on].next) == char_on)
|
||||
return;
|
||||
char_on = selectval;
|
||||
break;
|
||||
|
||||
case KEY_ESCAPE:
|
||||
noFurtherInput = true;
|
||||
M_GoBack(0);
|
||||
return;
|
||||
|
||||
default:
|
||||
return;
|
||||
}
|
||||
S_StartSound(NULL, sfx_menu1);
|
||||
}
|
||||
|
||||
static void M_StartMarathon(INT32 choice)
|
||||
{
|
||||
(void)choice;
|
||||
marathontime = 0;
|
||||
marathonmode = MA_RUNNING|MA_INIT;
|
||||
if (cv_dummymarathon.value == 1)
|
||||
cursaveslot = MARATHONSLOT;
|
||||
if (!cv_dummycutscenes.value)
|
||||
marathonmode |= MA_NOCUTSCENES;
|
||||
M_ChoosePlayer(char_on);
|
||||
}
|
||||
|
||||
// Drawing function for Marathon menu
|
||||
void M_DrawMarathon(void)
|
||||
{
|
||||
INT32 i, x, y, cursory = 0, cnt, soffset = 0, w;
|
||||
UINT16 dispstatus;
|
||||
consvar_t *cv;
|
||||
const char *cvstring;
|
||||
char *work;
|
||||
angle_t fa;
|
||||
INT32 dupz = (vid.dupx < vid.dupy ? vid.dupx : vid.dupy), xspan = (vid.width/dupz), yspan = (vid.height/dupz), diffx = (xspan - BASEVIDWIDTH)/2, diffy = (yspan - BASEVIDHEIGHT)/2, maxy = BASEVIDHEIGHT + diffy;
|
||||
|
||||
// lactozilla: the renderer changed so recache patches
|
||||
if (needpatchrecache)
|
||||
M_CacheCharacterSelect();
|
||||
|
||||
curbgxspeed = 0;
|
||||
curbgyspeed = 18;
|
||||
|
||||
M_ChangeMenuMusic("spec8", true); // Eww, but needed for when user hits escape during demo playback
|
||||
|
||||
V_DrawFill(-diffx, -diffy, diffx+(BASEVIDWIDTH-190)/2, yspan, 158);
|
||||
V_DrawFill((BASEVIDWIDTH-190)/2, -diffy, 190, yspan, 31);
|
||||
V_DrawFill((BASEVIDWIDTH+190)/2, -diffy, diffx+(BASEVIDWIDTH-190)/2, yspan, 158);
|
||||
//M_DrawRecordAttackForeground();
|
||||
if (curfadevalue)
|
||||
V_DrawFadeScreen(0xFF00, curfadevalue);
|
||||
|
||||
x = (((BASEVIDWIDTH-82)/2)+11)<<FRACBITS;
|
||||
y = (((BASEVIDHEIGHT-82)/2)+12-10)<<FRACBITS;
|
||||
|
||||
cnt = (36*(recatkdrawtimer<<FRACBITS))/TICRATE;
|
||||
fa = (FixedAngle(cnt)>>ANGLETOFINESHIFT) & FINEMASK;
|
||||
y -= (10*FINECOSINE(fa));
|
||||
|
||||
recatkdrawtimer++;
|
||||
|
||||
soffset = cnt = (recatkdrawtimer%50);
|
||||
if (!useBlackRock)
|
||||
{
|
||||
if (cnt > 8)
|
||||
cnt = 8;
|
||||
V_DrawFixedPatch(x+(6<<FRACBITS), y, FRACUNIT/2, (cnt&~1)<<(V_ALPHASHIFT-1), W_CachePatchName("RECCLOCK", PU_PATCH), NULL);
|
||||
}
|
||||
else if (cnt > 8)
|
||||
{
|
||||
cnt = 8;
|
||||
V_DrawFixedPatch(x, y, FRACUNIT, V_TRANSLUCENT, W_CachePatchName("ENDEGRK5", PU_PATCH), NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
V_DrawFixedPatch(x, y, FRACUNIT, cnt<<V_ALPHASHIFT, W_CachePatchName("ROID0000", PU_PATCH), NULL);
|
||||
V_DrawFixedPatch(x, y, FRACUNIT, V_TRANSLUCENT, W_CachePatchName("ENDEGRK5", PU_PATCH), NULL);
|
||||
V_DrawFixedPatch(x, y, FRACUNIT, cnt<<V_ALPHASHIFT, W_CachePatchName("ENDEGRK0", PU_PATCH), NULL);
|
||||
}
|
||||
|
||||
{
|
||||
UINT8 col;
|
||||
i = 0;
|
||||
w = (((8-cnt)+1)/3)+1;
|
||||
w *= w;
|
||||
cursory = 0;
|
||||
while (i < cnt)
|
||||
{
|
||||
i++;
|
||||
col = 158+((cnt-i)/3);
|
||||
if (col >= 160)
|
||||
col = 253;
|
||||
V_DrawFill(((BASEVIDWIDTH-190)/2)-cursory-w, -diffy, w, yspan, col);
|
||||
V_DrawFill(((BASEVIDWIDTH+190)/2)+cursory, -diffy, w, yspan, col);
|
||||
cursory += w;
|
||||
w *= 2;
|
||||
}
|
||||
}
|
||||
|
||||
w = char_scroll + (((8-cnt)*(8-cnt))<<(FRACBITS-5));
|
||||
if (soffset == 50-1)
|
||||
w += FRACUNIT/2;
|
||||
|
||||
{
|
||||
patch_t *fg = W_CachePatchName("RECATKFG", PU_PATCH);
|
||||
INT32 trans = V_60TRANS+((cnt&~3)<<(V_ALPHASHIFT-2));
|
||||
INT32 height = (SHORT(fg->height)/2);
|
||||
char patchname[7] = "CEMGx0";
|
||||
|
||||
dupz = (w*7)/6; //(w*42*120)/(360*6); -- I don't know why this works but I'm not going to complain.
|
||||
dupz = ((dupz>>FRACBITS) % height);
|
||||
y = height/2;
|
||||
while (y+dupz >= -diffy)
|
||||
y -= height;
|
||||
while (y-2-dupz < maxy)
|
||||
{
|
||||
V_DrawFixedPatch(((BASEVIDWIDTH-190)<<(FRACBITS-1)), (y-2-dupz)<<FRACBITS, FRACUNIT/2, trans, fg, NULL);
|
||||
V_DrawFixedPatch(((BASEVIDWIDTH+190)<<(FRACBITS-1)), (y+dupz)<<FRACBITS, FRACUNIT/2, trans|V_FLIP, fg, NULL);
|
||||
y += height;
|
||||
}
|
||||
|
||||
trans = V_40TRANS+((cnt&~1)<<(V_ALPHASHIFT-1));
|
||||
|
||||
for (i = 0; i < 7; ++i)
|
||||
{
|
||||
fa = (FixedAngle(w)>>ANGLETOFINESHIFT) & FINEMASK;
|
||||
x = (BASEVIDWIDTH<<(FRACBITS-1)) + (60*FINESINE(fa));
|
||||
y = ((BASEVIDHEIGHT+16-20)<<(FRACBITS-1)) - (60*FINECOSINE(fa));
|
||||
w += (360<<FRACBITS)/7;
|
||||
|
||||
patchname[4] = 'A'+(char)i;
|
||||
V_DrawFixedPatch(x, y, FRACUNIT, trans, W_CachePatchName(patchname, PU_PATCH), NULL);
|
||||
}
|
||||
|
||||
height = 18; // prevents the need for the next line
|
||||
//dupz = (w*height)/18;
|
||||
dupz = ((w>>FRACBITS) % height);
|
||||
y = dupz+(height/4);
|
||||
x = 105+dupz;
|
||||
while (y >= -diffy)
|
||||
{
|
||||
x -= height;
|
||||
y -= height;
|
||||
}
|
||||
while (y-dupz < maxy && x < (xspan/2))
|
||||
{
|
||||
V_DrawFill((BASEVIDWIDTH/2)-x-height, -diffy, height, diffy+y+height, 153);
|
||||
V_DrawFill((BASEVIDWIDTH/2)+x, (maxy-y)-height, height, height+y, 153);
|
||||
y += height;
|
||||
x += height;
|
||||
}
|
||||
}
|
||||
|
||||
if (!soffset)
|
||||
{
|
||||
char_scroll += (360<<FRACBITS)/42; // like a clock, ticking at 42bpm!
|
||||
if (char_scroll >= 360<<FRACBITS)
|
||||
char_scroll -= 360<<FRACBITS;
|
||||
if (recatkdrawtimer > (10*TICRATE))
|
||||
recatkdrawtimer -= (10*TICRATE);
|
||||
}
|
||||
|
||||
//M_DrawMenuTitle();
|
||||
|
||||
// draw menu (everything else goes on top of it)
|
||||
// Sadly we can't just use generic mode menus because we need some extra hacks
|
||||
x = currentMenu->x;
|
||||
y = currentMenu->y;
|
||||
|
||||
dispstatus = (currentMenu->menuitems[marathonplayer].status & IT_DISPLAY);
|
||||
if (dispstatus == IT_STRING || dispstatus == IT_WHITESTRING)
|
||||
{
|
||||
soffset = 68;
|
||||
if (description[char_on].charpic->width >= 256)
|
||||
V_DrawTinyScaledPatch(224, 120, 0, description[char_on].charpic);
|
||||
else
|
||||
V_DrawSmallScaledPatch(224, 120, 0, description[char_on].charpic);
|
||||
}
|
||||
else
|
||||
soffset = 0;
|
||||
|
||||
for (i = 0; i < currentMenu->numitems; ++i)
|
||||
{
|
||||
dispstatus = (currentMenu->menuitems[i].status & IT_DISPLAY);
|
||||
if (dispstatus != IT_STRING && dispstatus != IT_WHITESTRING)
|
||||
continue;
|
||||
|
||||
y = currentMenu->y+currentMenu->menuitems[i].alphaKey;
|
||||
if (i == itemOn)
|
||||
cursory = y;
|
||||
|
||||
V_DrawString(x, y, (dispstatus == IT_WHITESTRING) ? V_YELLOWMAP : 0 , currentMenu->menuitems[i].text);
|
||||
|
||||
cv = NULL;
|
||||
cvstring = NULL;
|
||||
work = NULL;
|
||||
if ((currentMenu->menuitems[i].status & IT_TYPE) == IT_CVAR)
|
||||
{
|
||||
cv = (consvar_t *)currentMenu->menuitems[i].itemaction;
|
||||
cvstring = cv->string;
|
||||
}
|
||||
else if (i == marathonplayer)
|
||||
{
|
||||
if (description[char_on].displayname[0])
|
||||
{
|
||||
work = Z_StrDup(description[char_on].displayname);
|
||||
cnt = 0;
|
||||
while (work[cnt])
|
||||
{
|
||||
if (work[cnt] == '\n')
|
||||
work[cnt] = ' ';
|
||||
cnt++;
|
||||
}
|
||||
cvstring = work;
|
||||
}
|
||||
else
|
||||
cvstring = description[char_on].skinname;
|
||||
}
|
||||
|
||||
// Cvar specific handling
|
||||
if (cvstring)
|
||||
{
|
||||
INT32 flags = V_YELLOWMAP;
|
||||
if (cv == &cv_dummymarathon && cv->value == 2) // ultimate_selectable
|
||||
flags = V_REDMAP;
|
||||
|
||||
// Should see nothing but strings
|
||||
if (cv == &cv_dummymarathon && cv->value == 1)
|
||||
{
|
||||
w = V_ThinStringWidth(cvstring, 0);
|
||||
V_DrawThinString(BASEVIDWIDTH - x - soffset - w, y+1, flags, cvstring);
|
||||
}
|
||||
else
|
||||
{
|
||||
w = V_StringWidth(cvstring, 0);
|
||||
V_DrawString(BASEVIDWIDTH - x - soffset - w, y, flags, cvstring);
|
||||
}
|
||||
if (i == itemOn)
|
||||
{
|
||||
V_DrawCharacter(BASEVIDWIDTH - x - soffset - 10 - w - (skullAnimCounter/5), y,
|
||||
'\x1C' | V_YELLOWMAP, false);
|
||||
V_DrawCharacter(BASEVIDWIDTH - x - soffset + 2 + (skullAnimCounter/5), y,
|
||||
'\x1D' | V_YELLOWMAP, false);
|
||||
}
|
||||
if (work)
|
||||
Z_Free(work);
|
||||
}
|
||||
}
|
||||
|
||||
// DRAW THE SKULL CURSOR
|
||||
V_DrawScaledPatch(currentMenu->x - 24, cursory, 0, W_CachePatchName("M_CURSOR", PU_PATCH));
|
||||
V_DrawString(currentMenu->x, cursory, V_YELLOWMAP, currentMenu->menuitems[itemOn].text);
|
||||
|
||||
// Draw press ESC to exit string on main record attack menu
|
||||
V_DrawString(104-72, 180, V_TRANSLUCENT, M_GetText("Press ESC to exit"));
|
||||
}
|
||||
|
||||
// ========
|
||||
// END GAME
|
||||
// ========
|
||||
|
@ -12578,6 +13007,7 @@ void M_QuitResponse(INT32 ch)
|
|||
if (!(netgame || cv_debug))
|
||||
{
|
||||
S_ResetCaptions();
|
||||
marathonmode = 0;
|
||||
|
||||
mrand = M_RandomKey(sizeof(quitsounds)/sizeof(INT32));
|
||||
if (quitsounds[mrand]) S_StartSound(NULL, quitsounds[mrand]);
|
||||
|
|
|
@ -61,6 +61,8 @@ typedef enum
|
|||
MN_SP_NIGHTS_REPLAY,
|
||||
MN_SP_NIGHTS_GHOST,
|
||||
|
||||
MN_SP_MARATHON,
|
||||
|
||||
// Multiplayer
|
||||
MN_MP_MAIN,
|
||||
MN_MP_SPLITSCREEN, // SplitServer
|
||||
|
@ -419,6 +421,7 @@ extern INT16 char_on, startchar;
|
|||
|
||||
#define MAXSAVEGAMES 31
|
||||
#define NOSAVESLOT 0 //slot where Play Without Saving appears
|
||||
#define MARATHONSLOT 420 // just has to be nonzero, but let's use one that'll show up as an obvious error if something goes wrong while not using our existing saves
|
||||
|
||||
#define BwehHehHe() S_StartSound(NULL, sfx_bewar1+M_RandomKey(4)) // Bweh heh he
|
||||
|
||||
|
|
|
@ -3807,10 +3807,10 @@ static inline void P_UnArchiveSPGame(INT16 mapoverride)
|
|||
if (mapoverride != 0)
|
||||
{
|
||||
gamemap = mapoverride;
|
||||
gamecomplete = true;
|
||||
gamecomplete = 1;
|
||||
}
|
||||
else
|
||||
gamecomplete = false;
|
||||
gamecomplete = 0;
|
||||
|
||||
// gamemap changed; we assume that its map header is always valid,
|
||||
// so make it so
|
||||
|
|
|
@ -217,6 +217,7 @@ static void P_ClearSingleMapHeaderInfo(INT16 i)
|
|||
mapheaderinfo[num]->actnum = 0;
|
||||
mapheaderinfo[num]->typeoflevel = 0;
|
||||
mapheaderinfo[num]->nextlevel = (INT16)(i + 1);
|
||||
mapheaderinfo[num]->marathonnext = 0;
|
||||
mapheaderinfo[num]->startrings = 0;
|
||||
mapheaderinfo[num]->sstimer = 90;
|
||||
mapheaderinfo[num]->ssspheres = 1;
|
||||
|
@ -3181,7 +3182,7 @@ static boolean CanSaveLevel(INT32 mapnum)
|
|||
// Any levels that have the savegame flag can save normally.
|
||||
// If the game is complete for this save slot, then any level can save!
|
||||
// On the other side of the spectrum, if lastmaploaded is 0, then the save file has only just been created and needs to save ASAP!
|
||||
return (mapheaderinfo[mapnum-1]->levelflags & LF_SAVEGAME || gamecomplete || !lastmaploaded);
|
||||
return (mapheaderinfo[mapnum-1]->levelflags & LF_SAVEGAME || (gamecomplete != 0) || marathonmode || !lastmaploaded);
|
||||
}
|
||||
|
||||
static void P_RunSpecialStageWipe(void)
|
||||
|
|
32
src/screen.c
32
src/screen.c
|
@ -622,3 +622,35 @@ void SCR_ClosedCaptions(void)
|
|||
va("%c [%s]", dot, (closedcaptions[i].s->caption[0] ? closedcaptions[i].s->caption : closedcaptions[i].s->name)));
|
||||
}
|
||||
}
|
||||
|
||||
void SCR_DisplayMarathonInfo(void)
|
||||
{
|
||||
INT32 flags = V_SNAPTOBOTTOM;
|
||||
static tic_t entertic, oldentertics = 0;
|
||||
const char *str;
|
||||
#if 0 // eh, this probably isn't going to be a problem
|
||||
if (((signed)marathontime) < 0)
|
||||
{
|
||||
flags |= V_REDMAP;
|
||||
str = "No waiting out the clock to submit a bogus time.";
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
entertic = I_GetTime();
|
||||
if (gamecomplete)
|
||||
flags |= V_YELLOWMAP;
|
||||
else if (marathonmode & MA_INIT)
|
||||
marathonmode &= ~MA_INIT;
|
||||
else
|
||||
marathontime += entertic - oldentertics;
|
||||
str = va("%i:%02i:%02i.%02i",
|
||||
G_TicsToHours(marathontime),
|
||||
G_TicsToMinutes(marathontime, false),
|
||||
G_TicsToSeconds(marathontime),
|
||||
G_TicsToCentiseconds(marathontime));
|
||||
oldentertics = entertic;
|
||||
}
|
||||
V_DrawPromptBack(-8, cons_backcolor.value);
|
||||
V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-8, flags, str);
|
||||
}
|
||||
|
|
|
@ -206,5 +206,6 @@ FUNCMATH boolean SCR_IsAspectCorrect(INT32 width, INT32 height);
|
|||
void SCR_DisplayTicRate(void);
|
||||
void SCR_ClosedCaptions(void);
|
||||
void SCR_DisplayLocalPing(void);
|
||||
void SCR_DisplayMarathonInfo(void);
|
||||
#undef DNWH
|
||||
#endif //__SCREEN_H__
|
||||
|
|
|
@ -1214,6 +1214,9 @@ void I_FinishUpdate(void)
|
|||
if (cv_showping.value && netgame && consoleplayer != serverplayer)
|
||||
SCR_DisplayLocalPing();
|
||||
|
||||
if (marathonmode)
|
||||
SCR_DisplayMarathonInfo();
|
||||
|
||||
if (rendermode == render_soft && screens[0])
|
||||
{
|
||||
SDL_Rect rect;
|
||||
|
|
|
@ -763,7 +763,7 @@ static void ST_drawTime(void)
|
|||
ST_DrawPatchFromHud(HUD_TIMECOLON, sbocolon, V_HUDTRANS); // Colon
|
||||
ST_DrawPadNumFromHud(HUD_SECONDS, seconds, 2, V_HUDTRANS); // Seconds
|
||||
|
||||
if (cv_timetic.value == 1 || cv_timetic.value == 2 || modeattacking) // there's not enough room for tics in splitscreen, don't even bother trying!
|
||||
if (cv_timetic.value == 1 || cv_timetic.value == 2 || modeattacking || marathonmode)
|
||||
{
|
||||
ST_DrawPatchFromHud(HUD_TIMETICCOLON, sboperiod, V_HUDTRANS); // Period
|
||||
ST_DrawPadNumFromHud(HUD_TICS, tictrn, 2, V_HUDTRANS); // Tics
|
||||
|
@ -1445,7 +1445,7 @@ static void ST_drawPowerupHUD(void)
|
|||
// ---------
|
||||
|
||||
// Let's have a power-like icon to represent finishing the level!
|
||||
if (stplyr->pflags & PF_FINISHED && cv_exitmove.value)
|
||||
if (stplyr->pflags & PF_FINISHED && cv_exitmove.value && multiplayer)
|
||||
{
|
||||
finishoffs[q] = ICONSEP;
|
||||
V_DrawSmallScaledPatch(offs, hudinfo[HUD_POWERUPS].y, V_PERPLAYER|hudinfo[HUD_POWERUPS].f|V_HUDTRANS, fnshico);
|
||||
|
|
|
@ -1870,7 +1870,10 @@ void V_DrawPromptBack(INT32 boxheight, INT32 color)
|
|||
|
||||
if (color >= 256 && color < 512)
|
||||
{
|
||||
boxheight = ((boxheight * 4) + (boxheight/2)*5);
|
||||
if (boxheight < 0)
|
||||
boxheight = -boxheight;
|
||||
else // 4 lines of space plus gaps between and some leeway
|
||||
boxheight = ((boxheight * 4) + (boxheight/2)*5);
|
||||
V_DrawFill((BASEVIDWIDTH-(vid.width/vid.dupx))/2, BASEVIDHEIGHT-boxheight, (vid.width/vid.dupx),boxheight, (color-256)|V_SNAPTOBOTTOM);
|
||||
return;
|
||||
}
|
||||
|
@ -1917,8 +1920,11 @@ void V_DrawPromptBack(INT32 boxheight, INT32 color)
|
|||
|
||||
// heavily simplified -- we don't need to know x or y position,
|
||||
// just the start and stop positions
|
||||
deststop = screens[0] + vid.rowbytes * vid.height;
|
||||
buf = deststop - vid.rowbytes * ((boxheight * 4) + (boxheight/2)*5); // 4 lines of space plus gaps between and some leeway
|
||||
buf = deststop = screens[0] + vid.rowbytes * vid.height;
|
||||
if (boxheight < 0)
|
||||
buf += vid.rowbytes * boxheight;
|
||||
else // 4 lines of space plus gaps between and some leeway
|
||||
buf -= vid.rowbytes * ((boxheight * 4) + (boxheight/2)*5);
|
||||
for (; buf < deststop; ++buf)
|
||||
*buf = promptbgmap[*buf];
|
||||
}
|
||||
|
|
|
@ -377,6 +377,9 @@ void I_FinishUpdate(void)
|
|||
if (cv_showping.value && netgame && consoleplayer != serverplayer)
|
||||
SCR_DisplayLocalPing();
|
||||
|
||||
if (marathonmode)
|
||||
SCR_DisplayMarathonInfo();
|
||||
|
||||
//
|
||||
if (bDIBMode)
|
||||
{
|
||||
|
|
|
@ -392,7 +392,7 @@ dontdrawbg:
|
|||
if (gottoken) // first to be behind everything else
|
||||
Y_IntermissionTokenDrawer();
|
||||
|
||||
if (!splitscreen)
|
||||
if (!splitscreen) // there's not enough room in splitscreen, don't even bother trying!
|
||||
{
|
||||
// draw score
|
||||
ST_DrawPatchFromHud(HUD_SCORE, sboscore);
|
||||
|
@ -414,7 +414,7 @@ dontdrawbg:
|
|||
ST_DrawPatchFromHud(HUD_TIMECOLON, sbocolon); // Colon
|
||||
ST_DrawPadNumFromHud(HUD_SECONDS, seconds, 2); // Seconds
|
||||
|
||||
if (cv_timetic.value == 1 || cv_timetic.value == 2 || modeattacking) // there's not enough room for tics in splitscreen, don't even bother trying!
|
||||
if (cv_timetic.value == 1 || cv_timetic.value == 2 || modeattacking || marathonmode)
|
||||
{
|
||||
ST_DrawPatchFromHud(HUD_TIMETICCOLON, sboperiod); // Period
|
||||
ST_DrawPadNumFromHud(HUD_TICS, tictrn, 2); // Tics
|
||||
|
@ -1004,7 +1004,7 @@ void Y_Ticker(void)
|
|||
{
|
||||
INT32 i;
|
||||
UINT32 oldscore = data.coop.score;
|
||||
boolean skip = false;
|
||||
boolean skip = (marathonmode) ? true : false;
|
||||
boolean anybonuses = false;
|
||||
|
||||
if (!intertic) // first time only
|
||||
|
@ -1080,7 +1080,7 @@ void Y_Ticker(void)
|
|||
{
|
||||
INT32 i;
|
||||
UINT32 oldscore = data.spec.score;
|
||||
boolean skip = false, super = false, anybonuses = false;
|
||||
boolean skip = (marathonmode) ? true : false, super = false, anybonuses = false;
|
||||
|
||||
if (!intertic) // first time only
|
||||
{
|
||||
|
|
Loading…
Reference in a new issue