mirror of
https://github.com/KartKrewDev/RingRacers.git
synced 2025-10-30 08:01:28 +00:00
473 lines
13 KiB
C
473 lines
13 KiB
C
/// \file menus/play-local-race-time-attack.c
|
|
/// \brief Race Time Attack Menu
|
|
|
|
#include "../k_menu.h"
|
|
#include "../r_local.h" // SplitScreen_OnChange
|
|
#include "../s_sound.h"
|
|
#include "../f_finale.h" // F_WipeStartScreen
|
|
#include "../v_video.h"
|
|
#include "../d_main.h" // srb2home
|
|
#include "../m_misc.h" // M_MkdirEach
|
|
#include "../z_zone.h" // Z_StrDup/Z_Free
|
|
|
|
// see ta_e
|
|
menuitem_t PLAY_TimeAttack[] =
|
|
{
|
|
{IT_STRING | IT_SUBMENU, "Replay...", NULL, NULL, {.submenu = &PLAY_TAReplayDef}, 0, 0},
|
|
{IT_STRING | IT_SUBMENU, "Guest...", NULL, NULL, {.submenu = &PLAY_TAReplayGuestDef}, 0, 0},
|
|
{IT_STRING | IT_SUBMENU, "Ghosts...", NULL, NULL, {.submenu = &PLAY_TAGhostsDef}, 0, 0},
|
|
{IT_HEADERTEXT|IT_HEADER, "", NULL, NULL, {NULL}, 0, 0},
|
|
{IT_STRING | IT_CALL, "Start", NULL, NULL, {.routine = M_StartTimeAttack}, 0, 0},
|
|
};
|
|
|
|
menu_t PLAY_TimeAttackDef = {
|
|
sizeof(PLAY_TimeAttack) / sizeof(menuitem_t),
|
|
&PLAY_LevelSelectDef,
|
|
0,
|
|
PLAY_TimeAttack,
|
|
0, 0,
|
|
0, 0,
|
|
NULL,
|
|
2, 5,
|
|
M_DrawTimeAttack,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL
|
|
};
|
|
|
|
|
|
typedef enum
|
|
{
|
|
tareplay_besttime = 0,
|
|
tareplay_bestlap,
|
|
tareplay_gap1,
|
|
tareplay_last,
|
|
tareplay_guest,
|
|
tareplay_staff,
|
|
tareplay_gap2,
|
|
tareplay_back
|
|
} tareplay_e;
|
|
|
|
menuitem_t PLAY_TAReplay[] =
|
|
{
|
|
{IT_STRING | IT_CALL, "Replay Best Time", NULL, NULL, {.routine = M_ReplayTimeAttack}, 0, 0},
|
|
{IT_STRING | IT_CALL, "Replay Best Lap", NULL, NULL, {.routine = M_ReplayTimeAttack}, 0, 0},
|
|
{IT_HEADERTEXT|IT_HEADER, "", NULL, NULL, {NULL}, 0, 0},
|
|
{IT_STRING | IT_CALL, "Replay Last", NULL, NULL, {.routine = M_ReplayTimeAttack}, 0, 0},
|
|
{IT_STRING | IT_CALL, "Replay Guest", NULL, NULL, {.routine = M_ReplayTimeAttack}, 0, 0},
|
|
{IT_STRING | IT_ARROWS, "Replay Staff", NULL, NULL, {.routine = M_HandleStaffReplay}, 0, 0},
|
|
{IT_HEADERTEXT|IT_HEADER, "", NULL, NULL, {NULL}, 0, 0},
|
|
|
|
{IT_STRING | IT_SUBMENU, "Back", NULL, NULL, {.submenu = &PLAY_TimeAttackDef}, 0, 0},
|
|
};
|
|
|
|
menu_t PLAY_TAReplayDef = {
|
|
sizeof(PLAY_TAReplay) / sizeof(menuitem_t),
|
|
&PLAY_TimeAttackDef,
|
|
0,
|
|
PLAY_TAReplay,
|
|
0, 0,
|
|
0, 0,
|
|
NULL,
|
|
2, 5,
|
|
M_DrawTimeAttack,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL
|
|
};
|
|
|
|
typedef enum
|
|
{
|
|
taguest_header = 0,
|
|
taguest_besttime,
|
|
taguest_bestlap,
|
|
taguest_last,
|
|
taguest_gap1,
|
|
taguest_delete,
|
|
taguest_gap2,
|
|
taguest_back
|
|
} taguest_e;
|
|
|
|
menuitem_t PLAY_TAReplayGuest[] =
|
|
{
|
|
{IT_HEADERTEXT|IT_HEADER, "Save as guest...", NULL, NULL, {NULL}, 0, 0},
|
|
|
|
{IT_STRING | IT_CALL, "Best Time", NULL, NULL, {.routine = M_SetGuestReplay}, 0, 0},
|
|
{IT_STRING | IT_CALL, "Best Lap", NULL, NULL, {.routine = M_SetGuestReplay}, 0, 0},
|
|
{IT_STRING | IT_CALL, "Last Run", NULL, NULL, {.routine = M_SetGuestReplay}, 0, 0},
|
|
|
|
{IT_HEADERTEXT|IT_HEADER, "", NULL, NULL, {NULL}, 0, 0},
|
|
{IT_STRING | IT_CALL, "Delete Guest", NULL, NULL, {.routine = M_SetGuestReplay}, 0, 0},
|
|
|
|
{IT_HEADERTEXT|IT_HEADER, "", NULL, NULL, {NULL}, 0, 0},
|
|
{IT_STRING | IT_SUBMENU, "Back", NULL, NULL, {.submenu = &PLAY_TimeAttackDef}, 0, 0},
|
|
|
|
};
|
|
|
|
menu_t PLAY_TAReplayGuestDef = {
|
|
sizeof(PLAY_TAReplayGuest) / sizeof(menuitem_t),
|
|
&PLAY_TimeAttackDef,
|
|
0,
|
|
PLAY_TAReplayGuest,
|
|
0, 0,
|
|
0, 0,
|
|
NULL,
|
|
2, 5,
|
|
M_DrawTimeAttack,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL
|
|
};
|
|
|
|
typedef enum
|
|
{
|
|
taghost_besttime = 0,
|
|
taghost_bestlap,
|
|
taghost_last,
|
|
taghost_guest,
|
|
taghost_staff,
|
|
taghost_gap1,
|
|
taghost_back
|
|
} taghost_e;
|
|
|
|
menuitem_t PLAY_TAGhosts[] =
|
|
{
|
|
{IT_STRING | IT_CVAR, "Best Time", NULL, NULL, {.cvar = &cv_ghost_besttime}, 0, 0},
|
|
{IT_STRING | IT_CVAR, "Best Lap", NULL, NULL, {.cvar = &cv_ghost_bestlap}, 0, 0},
|
|
{IT_STRING | IT_CVAR, "Last", NULL, NULL, {.cvar = &cv_ghost_last}, 0, 0},
|
|
{IT_DISABLED, "Guest", NULL, NULL, {.cvar = &cv_ghost_guest}, 0, 0},
|
|
{IT_DISABLED, "Staff", NULL, NULL, {.cvar = &cv_ghost_staff}, 0, 0},
|
|
|
|
{IT_HEADERTEXT|IT_HEADER, "", NULL, NULL, {NULL}, 0, 0},
|
|
{IT_STRING | IT_SUBMENU, "Back", NULL, NULL, {.submenu = &PLAY_TimeAttackDef}, 0, 0},
|
|
};
|
|
|
|
menu_t PLAY_TAGhostsDef = {
|
|
sizeof(PLAY_TAGhosts) / sizeof(menuitem_t),
|
|
&PLAY_TimeAttackDef,
|
|
0,
|
|
PLAY_TAGhosts,
|
|
0, 0,
|
|
0, 0,
|
|
NULL,
|
|
2, 5,
|
|
M_DrawTimeAttack,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL
|
|
};
|
|
|
|
// autorecord demos for time attack
|
|
consvar_t cv_autorecord = CVAR_INIT ("autorecord", "Yes", 0, CV_YesNo, NULL);
|
|
|
|
CV_PossibleValue_t ghost_cons_t[] = {{0, "Hide"}, {1, "Show Character"}, {2, "Show All"}, {0, NULL}};
|
|
CV_PossibleValue_t ghost2_cons_t[] = {{0, "Hide"}, {1, "Show"}, {0, NULL}};
|
|
|
|
consvar_t cv_ghost_besttime = CVAR_INIT ("ghost_besttime", "Show All", CV_SAVE, ghost_cons_t, NULL);
|
|
consvar_t cv_ghost_bestlap = CVAR_INIT ("ghost_bestlap", "Show All", CV_SAVE, ghost_cons_t, NULL);
|
|
consvar_t cv_ghost_last = CVAR_INIT ("ghost_last", "Show All", CV_SAVE, ghost_cons_t, NULL);
|
|
consvar_t cv_ghost_guest = CVAR_INIT ("ghost_guest", "Show", CV_SAVE, ghost2_cons_t, NULL);
|
|
consvar_t cv_ghost_staff = CVAR_INIT ("ghost_staff", "Show", CV_SAVE, ghost2_cons_t, NULL);
|
|
|
|
// time attack stuff...
|
|
void M_PrepareTimeAttack(INT32 choice)
|
|
{
|
|
(void) choice;
|
|
|
|
// Gametype guess
|
|
if (levellist.guessgt != MAXGAMETYPES)
|
|
{
|
|
levellist.newgametype = levellist.guessgt;
|
|
if (!(gametypes[levellist.newgametype]->tol & mapheaderinfo[levellist.choosemap]->typeoflevel))
|
|
{
|
|
INT32 guess = G_GuessGametypeByTOL(mapheaderinfo[levellist.choosemap]->typeoflevel);
|
|
if (guess != -1)
|
|
levellist.newgametype = guess;
|
|
}
|
|
}
|
|
|
|
// Time-sticker Medals
|
|
G_UpdateTimeStickerMedals(levellist.choosemap, false);
|
|
|
|
// Menu options
|
|
{
|
|
// see also p_setup.c's P_LoadRecordGhosts
|
|
char *gpath = Z_StrDup(va("%s"PATHSEP"media"PATHSEP"replay"PATHSEP"%s"PATHSEP"%s", srb2home, timeattackfolder, G_BuildMapName(levellist.choosemap+1)));
|
|
UINT8 active;
|
|
|
|
if (!gpath)
|
|
return;
|
|
|
|
active = false;
|
|
PLAY_TimeAttack[ta_guest].status = IT_DISABLED;
|
|
PLAY_TimeAttack[ta_replay].status = IT_DISABLED;
|
|
PLAY_TimeAttack[ta_ghosts].status = IT_DISABLED;
|
|
|
|
// Check if file exists, if not, disable options
|
|
PLAY_TAReplay[tareplay_besttime].status =
|
|
PLAY_TAReplayGuest[taguest_besttime].status = IT_DISABLED;
|
|
if (FIL_FileExists(va("%s-%s-time-best.lmp", gpath, cv_skin[0].string)))
|
|
{
|
|
PLAY_TAReplay[tareplay_besttime].status = IT_STRING|IT_CALL;
|
|
PLAY_TAReplayGuest[taguest_besttime].status = IT_STRING|IT_CALL;
|
|
active |= (1|2|4);
|
|
}
|
|
else if (PLAY_TAReplayGuestDef.lastOn == taguest_besttime)
|
|
PLAY_TAReplayGuestDef.lastOn = taguest_back;
|
|
|
|
PLAY_TAReplay[tareplay_bestlap].status =
|
|
PLAY_TAReplayGuest[taguest_bestlap].status =
|
|
PLAY_TAGhosts[taghost_bestlap].status = IT_DISABLED;
|
|
if ((gametypes[levellist.newgametype]->rules & GTR_CIRCUIT)
|
|
&& (mapheaderinfo[levellist.choosemap]->numlaps != 1)
|
|
&& FIL_FileExists(va("%s-%s-lap-best.lmp", gpath, cv_skin[0].string)))
|
|
{
|
|
PLAY_TAReplay[tareplay_bestlap].status = IT_STRING|IT_CALL;
|
|
PLAY_TAReplayGuest[taguest_bestlap].status = IT_STRING|IT_CALL;
|
|
PLAY_TAGhosts[taghost_bestlap].status = IT_STRING|IT_CVAR;
|
|
active |= (1|2|4);
|
|
}
|
|
else if (PLAY_TAReplayGuestDef.lastOn == taguest_bestlap)
|
|
PLAY_TAReplayGuestDef.lastOn = taguest_back;
|
|
|
|
PLAY_TAReplay[tareplay_last].status =
|
|
PLAY_TAReplayGuest[taguest_last].status = IT_DISABLED;
|
|
if (FIL_FileExists(va("%s-%s-last.lmp", gpath, cv_skin[0].string)))
|
|
{
|
|
PLAY_TAReplay[tareplay_last].status = IT_STRING|IT_CALL;
|
|
PLAY_TAReplayGuest[taguest_last].status = IT_STRING|IT_CALL;
|
|
active |= (1|2|4);
|
|
}
|
|
else if (PLAY_TAReplayGuestDef.lastOn == taguest_last)
|
|
PLAY_TAReplayGuestDef.lastOn = taguest_back;
|
|
|
|
PLAY_TAReplay[tareplay_guest].status =
|
|
PLAY_TAGhosts[taghost_guest].status =
|
|
PLAY_TAReplayGuest[taguest_delete].status = IT_DISABLED;
|
|
if (FIL_FileExists(va("%s-guest.lmp", gpath)))
|
|
{
|
|
PLAY_TAReplay[tareplay_guest].status = IT_STRING|IT_CALL;
|
|
PLAY_TAReplayGuest[taguest_delete].status = IT_STRING|IT_CALL;
|
|
PLAY_TAGhosts[taghost_guest].status = IT_STRING|IT_CVAR;
|
|
active |= (1|2|4);
|
|
}
|
|
else if (PLAY_TAReplayGuestDef.lastOn == taguest_delete)
|
|
PLAY_TAReplayGuestDef.lastOn = taguest_back;
|
|
|
|
PLAY_TAReplay[tareplay_staff].status =
|
|
PLAY_TAGhosts[taghost_staff].status = IT_DISABLED;
|
|
if (mapheaderinfo[levellist.choosemap]->ghostCount > 0)
|
|
{
|
|
PLAY_TAReplay[tareplay_staff].status = IT_STRING|IT_ARROWS;
|
|
PLAY_TAGhosts[taghost_staff].status = IT_STRING|IT_CVAR;
|
|
CV_SetValue(&cv_dummystaff, 0);
|
|
active |= 1|4;
|
|
}
|
|
|
|
if (active & 1)
|
|
PLAY_TimeAttack[ta_replay].status = IT_STRING|IT_SUBMENU;
|
|
else if (PLAY_TimeAttackDef.lastOn == ta_replay)
|
|
PLAY_TimeAttackDef.lastOn = ta_start;
|
|
if (active & 2)
|
|
PLAY_TimeAttack[ta_guest].status = IT_STRING|IT_SUBMENU;
|
|
else if (PLAY_TimeAttackDef.lastOn == ta_guest)
|
|
PLAY_TimeAttackDef.lastOn = ta_start;
|
|
//if (active & 4) -- for possible future use
|
|
PLAY_TimeAttack[ta_ghosts].status = IT_STRING|IT_SUBMENU;
|
|
/*else if (PLAY_TimeAttackDef.lastOn == ta_ghosts)
|
|
PLAY_TimeAttackDef.lastOn = ta_start;*/
|
|
|
|
Z_Free(gpath);
|
|
}
|
|
}
|
|
|
|
void M_HandleStaffReplay(INT32 choice)
|
|
{
|
|
if (choice == 2)
|
|
{
|
|
restoreMenu = &PLAY_TimeAttackDef;
|
|
|
|
M_ClearMenus(true);
|
|
demo.loadfiles = false;
|
|
demo.ignorefiles = true; // Just assume that record attack replays have the files needed
|
|
|
|
G_DoPlayDemo(va("%s/GHOST_%u", mapheaderinfo[levellist.choosemap]->lumpname, cv_dummystaff.value+1));
|
|
return;
|
|
}
|
|
|
|
M_ChangeCvarDirect(choice, &cv_dummystaff);
|
|
}
|
|
|
|
void M_ReplayTimeAttack(INT32 choice)
|
|
{
|
|
const char *which;
|
|
|
|
restoreMenu = &PLAY_TimeAttackDef;
|
|
|
|
M_ClearMenus(true);
|
|
demo.loadfiles = false;
|
|
demo.ignorefiles = true; // Just assume that record attack replays have the files needed
|
|
|
|
switch (choice)
|
|
{
|
|
default:
|
|
case tareplay_besttime:
|
|
which = "time-best";
|
|
break;
|
|
case tareplay_bestlap:
|
|
which = "lap-best";
|
|
break;
|
|
case tareplay_last:
|
|
which = "last";
|
|
break;
|
|
case tareplay_guest:
|
|
G_DoPlayDemo(va("%s"PATHSEP"media"PATHSEP"replay"PATHSEP"%s"PATHSEP"%s-guest.lmp", srb2home, timeattackfolder, G_BuildMapName(levellist.choosemap+1)));
|
|
return;
|
|
}
|
|
|
|
G_DoPlayDemo(va("%s"PATHSEP"media"PATHSEP"replay"PATHSEP"%s"PATHSEP"%s-%s-%s.lmp", srb2home, timeattackfolder, G_BuildMapName(levellist.choosemap+1), cv_skin[0].string, which));
|
|
}
|
|
|
|
static const char *TA_GuestReplay_Str = NULL;
|
|
|
|
static void M_WriteGuestReplay(INT32 ch)
|
|
{
|
|
char *gpath, *rguest;
|
|
|
|
UINT8 *buf;
|
|
size_t len = 0;
|
|
|
|
if (ch != MA_YES)
|
|
return;
|
|
|
|
gpath = Z_StrDup(va("%s"PATHSEP"media"PATHSEP"replay"PATHSEP"%s"PATHSEP"%s", srb2home, timeattackfolder, G_BuildMapName(levellist.choosemap+1)));
|
|
|
|
if (TA_GuestReplay_Str != NULL)
|
|
{
|
|
len = FIL_ReadFile(va("%s-%s-%s.lmp", gpath, cv_skin[0].string, TA_GuestReplay_Str), &buf);
|
|
if (!len)
|
|
{
|
|
M_StartMessage("Replay to copy no longer exists!", NULL, MM_NOTHING);
|
|
Z_Free(gpath);
|
|
return;
|
|
}
|
|
}
|
|
|
|
rguest = Z_StrDup(va("%s-guest.lmp", gpath));
|
|
|
|
if (FIL_FileExists(rguest))
|
|
{
|
|
//M_StopMessage(0);
|
|
remove(rguest);
|
|
}
|
|
|
|
if (len)
|
|
{
|
|
FIL_WriteFile(rguest, buf, len);
|
|
}
|
|
|
|
Z_Free(rguest);
|
|
Z_Free(gpath);
|
|
|
|
M_PrepareTimeAttack(0);
|
|
M_SetupNextMenu(&PLAY_TimeAttackDef, false);
|
|
|
|
// TODO the following isn't showing up and I'm not sure why
|
|
//M_StartMessage(va("Guest replay data %s.", (len ? "saved" : "erased")), NULL, MM_NOTHING);
|
|
}
|
|
|
|
|
|
void M_SetGuestReplay(INT32 choice)
|
|
{
|
|
switch (choice)
|
|
{
|
|
case taguest_besttime:
|
|
TA_GuestReplay_Str = "time-best";
|
|
break;
|
|
case taguest_bestlap:
|
|
TA_GuestReplay_Str = "lap-best";
|
|
break;
|
|
case taguest_last:
|
|
TA_GuestReplay_Str = "last";
|
|
break;
|
|
|
|
case taguest_delete:
|
|
default:
|
|
TA_GuestReplay_Str = NULL;
|
|
break;
|
|
}
|
|
|
|
if (FIL_FileExists(va("%s"PATHSEP"media"PATHSEP"replay"PATHSEP"%s"PATHSEP"%s-guest.lmp", srb2home, timeattackfolder, G_BuildMapName(levellist.choosemap+1))))
|
|
{
|
|
M_StartMessage(va("Are you sure you want to\n%s the guest replay data?\n\nPress (A) to confirm or (B) to cancel", (TA_GuestReplay_Str != NULL ? "overwrite" : "delete")), FUNCPTRCAST(M_WriteGuestReplay), MM_YESNO);
|
|
}
|
|
else if (TA_GuestReplay_Str != NULL)
|
|
{
|
|
M_WriteGuestReplay(MA_YES);
|
|
}
|
|
}
|
|
|
|
void M_StartTimeAttack(INT32 choice)
|
|
{
|
|
char *gpath;
|
|
char nameofdemo[256];
|
|
|
|
(void)choice;
|
|
|
|
modeattacking = ATTACKING_TIME;
|
|
|
|
if ((gametypes[levellist.newgametype]->rules & GTR_CIRCUIT)
|
|
&& (mapheaderinfo[levellist.choosemap]->numlaps != 1))
|
|
{
|
|
modeattacking |= ATTACKING_LAP;
|
|
}
|
|
|
|
// Still need to reset devmode
|
|
cht_debug = 0;
|
|
emeralds = 0;
|
|
|
|
if (demo.playback)
|
|
G_StopDemo();
|
|
if (metalrecording)
|
|
G_StopMetalDemo();
|
|
|
|
splitscreen = 0;
|
|
SplitScreen_OnChange();
|
|
|
|
S_StartSound(NULL, sfx_s3k63);
|
|
|
|
paused = false;
|
|
|
|
// Early fadeout to let the sound finish playing
|
|
F_WipeStartScreen();
|
|
V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31);
|
|
F_WipeEndScreen();
|
|
F_RunWipe(wipedefs[wipe_level_toblack], false, "FADEMAP0", false, false);
|
|
|
|
SV_StartSinglePlayerServer(levellist.newgametype, false);
|
|
|
|
gpath = va("%s"PATHSEP"media"PATHSEP"replay"PATHSEP"%s",
|
|
srb2home, timeattackfolder);
|
|
M_MkdirEach(gpath, M_PathParts(gpath) - 3, 0755);
|
|
|
|
strcat(gpath, PATHSEP);
|
|
strcat(gpath, G_BuildMapName(levellist.choosemap+1));
|
|
|
|
snprintf(nameofdemo, sizeof nameofdemo, "%s-%s-last", gpath, cv_skin[0].string);
|
|
|
|
if (!cv_autorecord.value)
|
|
remove(va("%s"PATHSEP"%s.lmp", srb2home, nameofdemo));
|
|
else
|
|
G_RecordDemo(nameofdemo);
|
|
|
|
restoreMenu = &PLAY_TimeAttackDef;
|
|
|
|
M_ClearMenus(true);
|
|
D_MapChange(levellist.choosemap+1, levellist.newgametype, (cv_dummygpencore.value == 1), 1, 1, false, false);
|
|
|
|
G_UpdateTimeStickerMedals(levellist.choosemap, true);
|
|
}
|