From 32bdd10dbf2da9ec3a1c7f83ee2fc89c9238e69b Mon Sep 17 00:00:00 2001 From: TehRealSalt Date: Fri, 4 Oct 2019 22:44:19 -0400 Subject: [PATCH] Cup definitions, updated menu to use them --- src/dehacked.c | 130 ++++++++++++++++++++++++++++++++++++++++ src/doomstat.h | 18 ++++++ src/g_game.c | 4 ++ src/k_menu.h | 13 +++- src/k_menudef.c | 10 ++-- src/k_menudraw.c | 46 +++++++++++---- src/k_menufunc.c | 151 +++++++++++++++++++++++++++++++++++------------ 7 files changed, 313 insertions(+), 59 deletions(-) diff --git a/src/dehacked.c b/src/dehacked.c index 724550443..1e7bf402d 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -1397,6 +1397,100 @@ static void readlevelheader(MYFILE *f, INT32 num) Z_Free(s); } +static void readcupheader(MYFILE *f, cupheader_t *cup) +{ + char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL); + char *word; + char *word2; + char *tmp; + INT32 i; + + do + { + if (myfgets(s, MAXLINELEN, f)) + { + if (s[0] == '\n') + break; + + // First remove trailing newline, if there is one + tmp = strchr(s, '\n'); + if (tmp) + *tmp = '\0'; + + tmp = strchr(s, '#'); + if (tmp) + *tmp = '\0'; + if (s == tmp) + continue; // Skip comment lines, but don't break. + + // Set / reset word, because some things (Lua.) move it + word = s; + + // Get the part before the " = " + tmp = strchr(s, '='); + if (tmp) + *(tmp-1) = '\0'; + else + break; + strupr(word); + + // Now get the part after + word2 = tmp += 2; + i = atoi(word2); // used for numerical settings + strupr(word2); + + if (fastcmp(word, "ICON")) + { + deh_strlcpy(cup->icon, word2, + sizeof(cup->icon), va("%s Cup: icon", cup->name)); + } + else if (fastcmp(word, "LEVELLIST")) + { + cup->numlevels = 0; + + tmp = strtok(word2,","); + do { + INT32 map = atoi(tmp); + + if (tmp[0] >= 'A' && tmp[0] <= 'Z' && tmp[2] == '\0') + map = M_MapNumber(tmp[0], tmp[1]); + + if (!map) + break; + + if (cup->numlevels >= MAXLEVELLIST) + deh_warning("%s Cup: reached max levellist (%d)\n", cup->name, MAXLEVELLIST); + + cup->levellist[cup->numlevels] = map; + cup->numlevels++; + } while((tmp = strtok(NULL,",")) != NULL); + } + else if (fastcmp(word, "BONUSGAME")) + { + // Convert to map number + if (word2[0] >= 'A' && word2[0] <= 'Z' && word2[2] == '\0') + i = M_MapNumber(word2[0], word2[1]); + cup->bonusgame = (INT16)i; + } + else if (fastcmp(word, "SPECIALSTAGE")) + { + // Convert to map number + if (word2[0] >= 'A' && word2[0] <= 'Z' && word2[2] == '\0') + i = M_MapNumber(word2[0], word2[1]); + cup->specialstage = (INT16)i; + } + else if (fastcmp(word, "EMERALDNUM")) + { + cup->emeraldnum = (INT16)i; + } + else + deh_warning("%s Cup: unknown word '%s'", cup->name, word); + } + } while (!myfeof(f)); // finish when the line is empty + + Z_Free(s); +} + static void readcutscenescene(MYFILE *f, INT32 num, INT32 scenenum) { char *s = Z_Calloc(MAXLINELEN, PU_STATIC, NULL); @@ -3558,6 +3652,42 @@ static void DEH_LoadDehackedFile(MYFILE *f, UINT16 wad) } DEH_WriteUndoline(word, word2, UNDO_HEADER); } + else if (fastcmp(word, "CUP")) + { + cupheader_t *cup = kartcupheaders; + cupheader_t *prev = NULL; + + while (cup) + { + if (fastcmp(cup->name, word2)) + { + // mark as a major mod if it replaces an already-existing cup + G_SetGameModified(multiplayer, true); + break; + } + + prev = cup; + cup = cup->next; + } + + // Nothing found, add to the end. + if (!cup) + { + cup = Z_Calloc(sizeof (cupheader_t), PU_STATIC, NULL); + cup->id = numkartcupheaders; + deh_strlcpy(cup->name, word2, + sizeof(cup->name), va("Cup header %s: name", word2)); + if (prev != NULL) + prev->next = cup; + if (kartcupheaders == NULL) + kartcupheaders = cup; + numkartcupheaders++; + CONS_Printf("Added cup %d ('%s')\n", cup->id, cup->name); + } + + readcupheader(f, cup); + DEH_WriteUndoline(word, word2, UNDO_HEADER); + } else if (fastcmp(word, "CUTSCENE")) { if (i > 0 && i < 129) diff --git a/src/doomstat.h b/src/doomstat.h index 25899aff3..944bbfd5a 100644 --- a/src/doomstat.h +++ b/src/doomstat.h @@ -291,6 +291,24 @@ typedef struct extern mapheader_t* mapheaderinfo[NUMMAPS]; +#define MAXLEVELLIST 5 + +typedef struct cupheader_s +{ + UINT16 id; ///< Cup ID + char name[15]; ///< Cup title (14 chars) + char icon[9]; ///< Name of the icon patch + INT16 levellist[MAXLEVELLIST]; ///< List of levels that belong to this cup + UINT8 numlevels; ///< Number of levels defined in levellist + INT16 bonusgame; ///< Map number to use for bonus game + INT16 specialstage; ///< Map number to use for special stage + UINT8 emeraldnum; ///< ID of Emerald to use for special stage (1-7 for Chaos Emeralds, 8-14 for Super Emeralds, 0 for no emerald) + struct cupheader_s *next; ///< Next cup in linked list +} cupheader_t; + +extern cupheader_t *kartcupheaders; // Start of cup linked list +extern UINT16 numkartcupheaders; + enum TypeOfLevel { TOL_SP = 0x01, ///< Single Player diff --git a/src/g_game.c b/src/g_game.c index 783d2b072..e669ec3a3 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -153,6 +153,10 @@ struct quake quake; // Map Header Information mapheader_t* mapheaderinfo[NUMMAPS] = {NULL}; +// Kart cup definitions +cupheader_t *kartcupheaders = NULL; +UINT16 numkartcupheaders = 0; + static boolean exitgame = false; static boolean retrying = false; diff --git a/src/k_menu.h b/src/k_menu.h index 8cb7001d1..401620c1a 100644 --- a/src/k_menu.h +++ b/src/k_menu.h @@ -292,26 +292,33 @@ boolean M_CharacterSelectQuit(void); #define CUPS_MAX (NUMMAPS / CUPS_MAPSPERCUP) #define CUPS_PAGES (CUPS_MAX / (CUPS_COLUMNS * CUPS_ROWS)) +#define CUPID (levellist_cupgrid.x + (levellist_cupgrid.y * CUPS_COLUMNS)) + +extern cupheader_t *selectedcup; +extern INT16 selectedcupnum; + extern struct levellist_cupgrid_s { UINT8 numcups; SINT8 x, y; SINT8 pageno; tic_t previewanim; + boolean grandprix; // Setup grand prix server after picking } levellist_cupgrid; extern struct levellist_scroll_s { - SINT8 cupid; SINT8 cursor; UINT16 y; UINT16 dest; + boolean timeattack; // Setup time attack menu after picking } levellist_scroll; boolean M_CanShowLevelInList(INT32 mapnum, INT32 gt); -void M_LevelSelectInit(INT32 choice); +INT32 M_CountLevelsToShowInList(INT32 gt); +INT32 M_GetFirstLevelInList(INT32 gt); +void M_LevelSelectInit(INT32 choice); void M_CupSelectHandler(INT32 choice); void M_CupSelectTick(void); - void M_LevelSelectHandler(INT32 choice); void M_LevelSelectTick(void); diff --git a/src/k_menudef.c b/src/k_menudef.c index 2fd2b7264..09cfa355b 100644 --- a/src/k_menudef.c +++ b/src/k_menudef.c @@ -85,13 +85,13 @@ menu_t PLAY_GamemodesDef = KARTGAMEMODEMENU(PLAY_GamemodesMenu, &PLAY_MainDef); menuitem_t PLAY_RaceGamemodesMenu[] = { {IT_STRING | IT_CALL, "Grand Prix", "Compete for the best rank over five races!", - NULL, M_LevelSelectInit, 0, 0}, + NULL, M_LevelSelectInit, 2, GT_RACE}, {IT_STRING | IT_CALL, "Match Race", "Play by your own rules in a specialized, single race!", - NULL, M_LevelSelectInit, 1, 0}, + NULL, M_LevelSelectInit, 0, GT_RACE}, {IT_STRING | IT_CALL, "Time Attack", "Record your best time on any track!", - NULL, M_LevelSelectInit, 2, 0}, + NULL, M_LevelSelectInit, 1, GT_RACE}, {IT_STRING | IT_CALL, "Back", NULL, NULL, M_GoBack, 0, 0}, }; @@ -157,10 +157,10 @@ menu_t PLAY_TimeAttackDef = { menuitem_t PLAY_BattleGamemodesMenu[] = { {IT_STRING | IT_CALL, "Survival", "It's last hedgehog standing in this free-for-all!", - NULL, M_LevelSelectInit, 3, 0}, + NULL, M_LevelSelectInit, 0, GT_MATCH}, {IT_STRING | IT_CALL, "Time Attack", "Bust up all of the capsules in record time!", - NULL, M_LevelSelectInit, 4, 0}, + NULL, M_LevelSelectInit, 1, GT_MATCH}, {IT_STRING | IT_CALL, "Back", NULL, NULL, M_GoBack, 0, 0}, }; diff --git a/src/k_menudraw.c b/src/k_menudraw.c index 257ddac6a..eed27ad28 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -875,20 +875,18 @@ void M_DrawCharacterSelect(void) // LEVEL SELECT -static void M_DrawCupPreview(INT16 y, UINT8 cupnum) +static void M_DrawCupPreview(INT16 y, cupheader_t *cup) { UINT8 i; INT16 x = -(levellist_cupgrid.previewanim % 82); - V_DrawFill(0, y, BASEVIDWIDTH, 54, 31); - - for (i = 0; i < 5; i++) + for (i = 0; i < cup->numlevels; i++) { lumpnum_t lumpnum; patch_t *PictureOfLevel; - UINT8 lvloff = (i + (levellist_cupgrid.previewanim / 82)) % 5; + UINT8 lvloff = (i + (levellist_cupgrid.previewanim / 82)) % cup->numlevels; - lumpnum = W_CheckNumForName(va("%sP", G_BuildMapName(1 + (cupnum * 5) + lvloff))); + lumpnum = W_CheckNumForName(va("%sP", G_BuildMapName(cup->levellist[lvloff] ))); if (lumpnum != LUMPERROR) PictureOfLevel = W_CachePatchNum(lumpnum, PU_CACHE); else @@ -901,6 +899,14 @@ static void M_DrawCupPreview(INT16 y, UINT8 cupnum) void M_DrawCupSelect(void) { UINT8 i, j; + cupheader_t *cup = kartcupheaders; + + while (cup) + { + if (cup->id == CUPID) + break; + cup = cup->next; + } for (i = 0; i < CUPS_COLUMNS; i++) { @@ -915,10 +921,14 @@ void M_DrawCupSelect(void) 0, W_CachePatchName("CUPCURS", PU_CACHE) ); + V_DrawFill(0, 146 + (12*menutransition.tics), BASEVIDWIDTH, 54, 31); V_DrawScaledPatch(0, 120 - (12*menutransition.tics), 0, W_CachePatchName("MENUHINT", PU_CACHE)); - M_DrawCupPreview(146 + (12*menutransition.tics), levellist_cupgrid.x + (levellist_cupgrid.y * CUPS_COLUMNS)); - V_DrawCenteredLSTitleLowString(BASEVIDWIDTH/2, 126 - (12*menutransition.tics), 0, "SNEAKER CUP"); + if (cup) + { + M_DrawCupPreview(146 + (12*menutransition.tics), cup); + V_DrawCenteredLSTitleLowString(BASEVIDWIDTH/2, 126 - (12*menutransition.tics), 0, va("%s Cup", cup->name)); + } } static void M_DrawHighLowLevelTitle(INT16 x, INT16 y, INT16 map) @@ -930,7 +940,7 @@ static void M_DrawHighLowLevelTitle(INT16 x, INT16 y, INT16 map) INT16 x2 = x; UINT8 i; - if (!mapheaderinfo[map]->lvlttl[0]) + if (!mapheaderinfo[map] || !mapheaderinfo[map]->lvlttl[0]) return; if (mapheaderinfo[map]->zonttl[0]) @@ -1035,7 +1045,8 @@ static void M_DrawLevelSelectBlock(INT16 x, INT16 y, INT16 map, boolean redblink void M_DrawLevelSelect(void) { - UINT8 i; + INT16 i; + INT16 start = M_GetFirstLevelInList(cv_newgametype.value)-1; INT16 t = (32*menutransition.tics), tay = 0; INT16 y = 80 - (12 * levellist_scroll.y); boolean tatransition = (menutransition.startmenu == &PLAY_TimeAttackDef || menutransition.endmenu == &PLAY_TimeAttackDef); @@ -1046,10 +1057,16 @@ void M_DrawLevelSelect(void) tay = t/2; } - for (i = 0; i < 5; i++) + for (i = 0; i < M_CountLevelsToShowInList(cv_newgametype.value); i++) { - INT16 map = 1 + (levellist_scroll.cupid * 5) + i; INT16 lvlx = t, lvly = y; + INT16 map = start + i; + + while (!M_CanShowLevelInList(map, cv_newgametype.value) && map < NUMMAPS) + map++; + + if (map >= NUMMAPS) + break; if (i == levellist_scroll.cursor && tatransition) { @@ -1066,7 +1083,10 @@ void M_DrawLevelSelect(void) } V_DrawScaledPatch(0, tay, 0, W_CachePatchName("MENUHINT", PU_CACHE)); - V_DrawCenteredLSTitleLowString(BASEVIDWIDTH/2, 6+tay, 0, "SNEAKER CUP"); + if (selectedcup) + V_DrawCenteredLSTitleLowString(BASEVIDWIDTH/2, 6+tay, 0, va("%s Cup", selectedcup->name)); + else + V_DrawCenteredLSTitleLowString(BASEVIDWIDTH/2, 6+tay, 0, va("%s Mode", Gametype_Names[cv_newgametype.value])); } void M_DrawTimeAttack(void) diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 47d60e615..e3f1d0fa9 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -2017,16 +2017,6 @@ boolean M_CharacterSelectQuit(void) // LEVEL SELECT -#if 0 -// Call before showing any level-select menus -static void M_PrepareLevelSelect(void) -{ - if (levellistmode != LLM_CREATESERVER) - CV_SetValue(&cv_nextmap, M_GetFirstLevelInList()); - else - Newgametype_OnChange(); // Make sure to start on an appropriate map if wads have been added -} - // // M_CanShowLevelInList // @@ -2055,10 +2045,17 @@ boolean M_CanShowLevelInList(INT32 mapnum, INT32 gt) if (gt == GT_RACE && (mapheaderinfo[mapnum]->typeoflevel & TOL_RACE)) { - if (levellist_cup != -1) + if (selectedcup && selectedcup->numlevels) { - if (mapnum < (1 + ((levellist_cup-1) * 5)) - || mapnum > (6 + ((levellist_cup-1) * 5)) + UINT8 i; + + for (i = 0; i < selectedcup->numlevels; i++) + { + if (mapnum == selectedcup->levellist[i]) + break; + } + + if (i == selectedcup->numlevels) return false; } @@ -2069,75 +2066,95 @@ boolean M_CanShowLevelInList(INT32 mapnum, INT32 gt) return false; } -static INT32 M_CountLevelsToShowInList(void) +INT32 M_CountLevelsToShowInList(INT32 gt) { INT32 mapnum, count = 0; for (mapnum = 0; mapnum < NUMMAPS; mapnum++) - if (M_CanShowLevelInList(mapnum, -1)) + if (M_CanShowLevelInList(mapnum, gt)) count++; return count; } -static INT32 M_GetFirstLevelInList(void) +INT32 M_GetFirstLevelInList(INT32 gt) { INT32 mapnum; for (mapnum = 0; mapnum < NUMMAPS; mapnum++) - if (M_CanShowLevelInList(mapnum, -1)) + if (M_CanShowLevelInList(mapnum, gt)) return mapnum + 1; return 1; } -#endif +cupheader_t *selectedcup = NULL; struct levellist_cupgrid_s levellist_cupgrid; struct levellist_scroll_s levellist_scroll; static void M_LevelSelectScrollDest(void) { + UINT16 m = M_CountLevelsToShowInList(cv_newgametype.value)-1; + levellist_scroll.dest = (6*levellist_scroll.cursor); if (levellist_scroll.dest < 3) levellist_scroll.dest = 3; - if (levellist_scroll.dest > (6*4)-3) - levellist_scroll.dest = (6*4)-3; + if (levellist_scroll.dest > (6*m)-3) + levellist_scroll.dest = (6*m)-3; } void M_LevelSelectInit(INT32 choice) { - UINT8 selecttype = currentMenu->menuitems[itemOn].mvar1; - (void)choice; - switch (selecttype) + switch (currentMenu->menuitems[itemOn].mvar1) { + case 0: + levellist_cupgrid.grandprix = false; + levellist_scroll.timeattack = false; + break; + case 1: + levellist_cupgrid.grandprix = false; + levellist_scroll.timeattack = true; + break; case 2: - CV_StealthSetValue(&cv_newgametype, GT_RACE); + levellist_cupgrid.grandprix = true; + levellist_scroll.timeattack = false; break; default: - break; + CONS_Alert(CONS_WARNING, "Bad level select init\n"); + return; } + CV_StealthSetValue(&cv_newgametype, currentMenu->menuitems[itemOn].mvar2); PLAY_CupSelectDef.prevMenu = currentMenu; - if (cv_newgametype.value != GT_RACE) - { - PLAY_LevelSelectDef.prevMenu = currentMenu; - M_SetupNextMenu(&PLAY_LevelSelectDef, false); - } - else + if (cv_newgametype.value == GT_RACE) { PLAY_LevelSelectDef.prevMenu = &PLAY_CupSelectDef; M_SetupNextMenu(&PLAY_CupSelectDef, false); } + else + { + selectedcup = NULL; + PLAY_LevelSelectDef.prevMenu = currentMenu; + M_SetupNextMenu(&PLAY_LevelSelectDef, false); + } } void M_CupSelectHandler(INT32 choice) { - UINT8 selcup = levellist_cupgrid.x + (levellist_cupgrid.y * CUPS_COLUMNS); + cupheader_t *newcup = kartcupheaders; + + while (newcup) + { + CONS_Printf("%d == %d?\n", newcup->id, CUPID); + if (newcup->id == CUPID) + break; + newcup = newcup->next; + } switch (choice) { @@ -2178,10 +2195,13 @@ void M_CupSelectHandler(INT32 choice) S_StartSound(NULL, sfx_s3k5b); break; case KEY_ENTER: - if (levellist_scroll.cupid != selcup) // Keep cursor position if you select the same cup again + if (!newcup) + break; + + if (!selectedcup || newcup->id != selectedcup->id) // Keep cursor position if you select the same cup again { levellist_scroll.cursor = 0; - levellist_scroll.cupid = selcup; + selectedcup = newcup; } M_LevelSelectScrollDest(); @@ -2208,6 +2228,10 @@ void M_CupSelectTick(void) void M_LevelSelectHandler(INT32 choice) { + INT16 start = M_GetFirstLevelInList(cv_newgametype.value)-1; + INT16 maxlevels = M_CountLevelsToShowInList(cv_newgametype.value); + INT16 map = start; + if (levellist_scroll.y != levellist_scroll.dest) return; @@ -2216,18 +2240,69 @@ void M_LevelSelectHandler(INT32 choice) case KEY_UPARROW: levellist_scroll.cursor--; if (levellist_scroll.cursor < 0) - levellist_scroll.cursor = 4; + levellist_scroll.cursor = maxlevels-1; S_StartSound(NULL, sfx_s3k5b); break; case KEY_DOWNARROW: levellist_scroll.cursor++; - if (levellist_scroll.cursor > 4) + if (levellist_scroll.cursor >= maxlevels) levellist_scroll.cursor = 0; S_StartSound(NULL, sfx_s3k5b); break; case KEY_ENTER: - CV_SetValue(&cv_nextmap, 1 + (levellist_scroll.cupid * 5) + levellist_scroll.cursor); - M_SetupNextMenu(&PLAY_TimeAttackDef, false); + map = start + levellist_scroll.cursor; + + while (!M_CanShowLevelInList(map, cv_newgametype.value) && map < NUMMAPS) + map++; + + if (map >= NUMMAPS) + break; + + CV_SetValue(&cv_nextmap, map); + + if (levellist_scroll.timeattack) + M_SetupNextMenu(&PLAY_TimeAttackDef, false); + else + { + UINT8 ssplayers = cv_splitplayers.value-1; + + netgame = false; + multiplayer = true; + + strncpy(connectedservername, cv_servername.string, MAXSERVERNAME); + + // Still need to reset devmode + cv_debug = 0; + + if (strlen(cv_dummyjoinpassword.string) > 0) + D_SetJoinPassword(cv_dummyjoinpassword.string); + else + joinpasswordset = false; + + if (demo.playback) + G_StopDemo(); + if (metalrecording) + G_StopMetalDemo(); + + if (!cv_nextmap.value) + CV_SetValue(&cv_nextmap, G_RandMap(G_TOLFlag(cv_newgametype.value), -1, false, 0, false, NULL)+1); + + if (cv_maxplayers.value < ssplayers+1) + CV_SetValue(&cv_maxplayers, ssplayers+1); + + if (splitscreen != ssplayers) + { + splitscreen = ssplayers; + SplitScreen_OnChange(); + } + + paused = false; + SV_StartSinglePlayerServer(); + multiplayer = true; // yeah, SV_StartSinglePlayerServer clobbers this... + D_MapChange(cv_nextmap.value, cv_newgametype.value, (cv_kartencore.value == 1), 1, 1, false, false); + + M_ClearMenus(true); + } S_StartSound(NULL, sfx_s3k63); break; case KEY_ESCAPE: