Merge branch 'battle-cups' into 'master'

Battle Cups + Re-order level select

Closes #372

See merge request KartKrew/Kart!826
This commit is contained in:
Sal 2022-12-19 04:43:35 +00:00
commit a3583f5a9d
7 changed files with 244 additions and 109 deletions

View file

@ -3021,6 +3021,28 @@ void readwipes(MYFILE *f)
// SRB2KART
//
static void invalidateacrosscups(UINT16 map)
{
cupheader_t *cup = kartcupheaders;
UINT8 i;
if (map >= nummapheaders)
return;
while (cup)
{
for (i = 0; i < CUPCACHE_MAX; i++)
{
if (cup->cachedlevels[i] != map)
continue;
cup->cachedlevels[i] = NEXTMAP_INVALID;
}
cup = cup->next;
}
mapheaderinfo[map]->cup = NULL;
}
void readcupheader(MYFILE *f, cupheader_t *cup)
{
char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL);
@ -3077,7 +3099,7 @@ void readcupheader(MYFILE *f, cupheader_t *cup)
cup->levellist[cup->numlevels] = NULL;
if (cup->cachedlevels[cup->numlevels] == NEXTMAP_INVALID)
continue;
mapheaderinfo[cup->cachedlevels[cup->numlevels]]->cup = NULL;
invalidateacrosscups(cup->cachedlevels[cup->numlevels]);
}
tmp = strtok(word2,",");
@ -3100,6 +3122,9 @@ void readcupheader(MYFILE *f, cupheader_t *cup)
cup->numbonus--;
Z_Free(cup->levellist[CUPCACHE_BONUS + cup->numbonus]);
cup->levellist[CUPCACHE_BONUS + cup->numbonus] = NULL;
if (cup->cachedlevels[CUPCACHE_BONUS + cup->numbonus] == NEXTMAP_INVALID)
continue;
invalidateacrosscups(cup->cachedlevels[CUPCACHE_BONUS + cup->numbonus]);
}
tmp = strtok(word2,",");
@ -3117,6 +3142,7 @@ void readcupheader(MYFILE *f, cupheader_t *cup)
}
else if (fastcmp(word, "SPECIALSTAGE"))
{
invalidateacrosscups(cup->cachedlevels[CUPCACHE_SPECIAL]);
Z_Free(cup->levellist[CUPCACHE_SPECIAL]);
cup->levellist[CUPCACHE_SPECIAL] = Z_StrDup(word2);
cup->cachedlevels[CUPCACHE_SPECIAL] = NEXTMAP_INVALID;

View file

@ -502,6 +502,8 @@ enum GameTypeRules
GTR_LIVES = 1<<18, // Lives system, players are forced to spectate during Game Over.
GTR_SPECIALBOTS = 1<<19, // Bot difficulty gets stronger between rounds, and the rival system is enabled.
GTR_NOCUPSELECT = 1<<20, // Your maps are not selected via cup. ...mutually exclusive with GTR_CAMPAIGN.
// free: to and including 1<<31
};

View file

@ -3416,29 +3416,26 @@ UINT32 G_TOLFlag(INT32 pgametype)
INT16 G_GetFirstMapOfGametype(UINT8 pgametype)
{
UINT8 i = 0;
INT16 mapnum = NEXTMAP_INVALID;
UINT32 tol = G_TOLFlag(pgametype);
if ((gametypedefaultrules[pgametype] & GTR_CAMPAIGN) && kartcupheaders)
{
mapnum = kartcupheaders->cachedlevels[0];
}
levellist.cupmode = (!(gametypedefaultrules[pgametype] & GTR_NOCUPSELECT));
levellist.timeattack = false;
if (mapnum >= nummapheaders)
if (levellist.cupmode)
{
UINT32 tolflag = G_TOLFlag(pgametype);
for (mapnum = 0; mapnum < nummapheaders; mapnum++)
cupheader_t *cup = kartcupheaders;
while (cup && mapnum >= nummapheaders)
{
if (!mapheaderinfo[mapnum])
continue;
if (mapheaderinfo[mapnum]->lumpnum == LUMPERROR)
continue;
if (!(mapheaderinfo[mapnum]->typeoflevel & tolflag))
continue;
if (mapheaderinfo[mapnum]->menuflags & LF2_HIDEINMENU)
continue;
break;
mapnum = M_GetFirstLevelInList(&i, tol, cup);
i = 0;
}
}
else
{
mapnum = M_GetFirstLevelInList(&i, tol, NULL);
}
return mapnum;
}
@ -3864,7 +3861,7 @@ static void G_GetNextMap(void)
UINT32 tolflag = G_TOLFlag(gametype);
register INT16 cm;
if (gametyperules & GTR_CAMPAIGN)
if (!(gametyperules & GTR_NOCUPSELECT))
{
cupheader_t *cup = mapheaderinfo[gamemap-1]->cup;
UINT8 gettingresult = 0;
@ -3891,6 +3888,12 @@ static void G_GetNextMap(void)
|| (!marathonmode && M_MapLocked(cm+1)))
continue;
// If the map is in multiple cups, only consider the first one valid.
if (mapheaderinfo[cm]->cup != cup)
{
continue;
}
// Grab the first valid after the map you're on
if (gettingresult)
{

View file

@ -679,8 +679,10 @@ void M_SetupRaceMenu(INT32 choice);
extern struct cupgrid_s {
SINT8 x, y;
SINT8 pageno;
UINT8 numpages;
size_t pageno;
cupheader_t **builtgrid;
size_t numpages;
size_t cappages;
tic_t previewanim;
boolean grandprix; // Setup grand prix server after picking
boolean netgame; // Start the game in an actual server
@ -693,13 +695,16 @@ extern struct levellist_s {
cupheader_t *selectedcup;
INT16 choosemap;
UINT8 newgametype;
UINT32 typeoflevel;
boolean cupmode;
boolean timeattack; // Setup time attack menu after picking
boolean netgame; // Start the game in an actual server
} levellist;
boolean M_CanShowLevelInList(INT16 mapnum, UINT8 gt);
INT16 M_CountLevelsToShowInList(UINT8 gt);
INT16 M_GetFirstLevelInList(UINT8 gt);
boolean M_CanShowLevelInList(INT16 mapnum, UINT32 tol, cupheader_t *cup);
UINT16 M_CountLevelsToShowInList(UINT32 tol, cupheader_t *cup);
UINT16 M_GetFirstLevelInList(UINT8 *i, UINT32 tol, cupheader_t *cup);
UINT16 M_GetNextLevelInList(UINT16 map, UINT8 *i, UINT32 tol, cupheader_t *cup);
void M_LevelSelectInit(INT32 choice);
void M_CupSelectHandler(INT32 choice);

View file

@ -1902,31 +1902,52 @@ void M_DrawRaceDifficulty(void)
static void M_DrawCupPreview(INT16 y, cupheader_t *cup)
{
UINT8 i;
const INT16 pad = ((vid.width/vid.dupx) - BASEVIDWIDTH)/2;
INT16 x = -(cupgrid.previewanim % 82) - pad;
UINT8 i = 0;
INT16 maxlevels = M_CountLevelsToShowInList(levellist.typeoflevel, cup);
INT16 x = -(cupgrid.previewanim % 82);
INT16 add;
INT16 map, start = M_GetFirstLevelInList(&i, levellist.typeoflevel, cup);
UINT8 starti = i;
V_DrawFill(0, y, BASEVIDWIDTH, 54, 31);
if (cup && (cup->unlockrequired == -1 || unlockables[cup->unlockrequired].unlocked))
{
i = (cupgrid.previewanim / 82) % cup->numlevels;
while (x < BASEVIDWIDTH + pad)
add = (cupgrid.previewanim / 82) % maxlevels;
map = start;
while (add > 0)
{
map = M_GetNextLevelInList(map, &i, levellist.typeoflevel, cup);
if (map >= nummapheaders)
{
break;
}
add--;
}
while (x < BASEVIDWIDTH)
{
INT32 cupLevelNum = cup->cachedlevels[i];
patch_t *PictureOfLevel = NULL;
if (cupLevelNum < nummapheaders && mapheaderinfo[cupLevelNum])
if (map >= nummapheaders)
{
PictureOfLevel = mapheaderinfo[cupLevelNum]->thumbnailPic;
map = start;
i = starti;
}
if (map < nummapheaders && mapheaderinfo[map])
{
PictureOfLevel = mapheaderinfo[map]->thumbnailPic;
}
if (!PictureOfLevel)
PictureOfLevel = blanklvl;
V_DrawSmallScaledPatch(x + 1, y+2, 0, PictureOfLevel);
i = (i+1) % cup->numlevels;
x += 82;
map = M_GetNextLevelInList(map, &i, levellist.typeoflevel, cup);
}
}
else
@ -1970,32 +1991,18 @@ static void M_DrawCupTitle(INT16 y, cupheader_t *cup)
void M_DrawCupSelect(void)
{
UINT8 i, j;
cupheader_t *cup = kartcupheaders;
while (cup)
{
if (cup->id == CUPMENU_CURSORID)
break;
cup = cup->next;
}
cupheader_t *cup = cupgrid.builtgrid[CUPMENU_CURSORID];
for (i = 0; i < CUPMENU_COLUMNS; i++)
{
for (j = 0; j < CUPMENU_ROWS; j++)
{
UINT8 id = (i + (j * CUPMENU_COLUMNS)) + (cupgrid.pageno * (CUPMENU_COLUMNS * CUPMENU_ROWS));
cupheader_t *iconcup = kartcupheaders;
size_t id = (i + (j * CUPMENU_COLUMNS)) + (cupgrid.pageno * (CUPMENU_COLUMNS * CUPMENU_ROWS));
cupheader_t *iconcup = cupgrid.builtgrid[id];
patch_t *patch = NULL;
INT16 x, y;
INT16 icony = 7;
while (iconcup)
{
if (iconcup->id == id)
break;
iconcup = iconcup->next;
}
if (!iconcup)
break;
@ -2167,9 +2174,9 @@ static void M_DrawLevelSelectBlock(INT16 x, INT16 y, INT16 map, boolean redblink
void M_DrawLevelSelect(void)
{
INT16 i;
INT16 start = M_GetFirstLevelInList(levellist.newgametype);
INT16 map = start;
INT16 i = 0;
UINT8 j = 0;
INT16 map = M_GetFirstLevelInList(&j, levellist.typeoflevel, levellist.selectedcup);
INT16 t = (64*menutransition.tics), tay = 0;
INT16 y = 80 - (12 * levellist.y);
boolean tatransition = ((menutransition.startmenu == &PLAY_TimeAttackDef || menutransition.endmenu == &PLAY_TimeAttackDef) && menutransition.tics);
@ -2180,13 +2187,10 @@ void M_DrawLevelSelect(void)
tay = t/2;
}
for (i = 0; i < M_CountLevelsToShowInList(levellist.newgametype); i++)
while (true)
{
INT16 lvlx = t, lvly = y;
while (!M_CanShowLevelInList(map, levellist.newgametype) && map < nummapheaders)
map++;
if (map >= nummapheaders)
break;
@ -2202,7 +2206,8 @@ void M_DrawLevelSelect(void)
);
y += 72;
map++;
i++;
map = M_GetNextLevelInList(map, &j, levellist.typeoflevel, levellist.selectedcup);
}
M_DrawCupTitle(tay, levellist.selectedcup);

View file

@ -3337,11 +3337,11 @@ void M_SetupDifficultySelect(INT32 choice)
// M_CanShowLevelInList
//
// Determines whether to show a given map in the various level-select lists.
// Set gt = -1 to ignore gametype.
//
boolean M_CanShowLevelInList(INT16 mapnum, UINT8 gt)
boolean M_CanShowLevelInList(INT16 mapnum, UINT32 tol, cupheader_t *cup)
{
UINT32 tolflag = G_TOLFlag(gt);
if (mapnum >= nummapheaders)
return false;
// Does the map exist?
if (!mapheaderinfo[mapnum])
@ -3359,7 +3359,7 @@ boolean M_CanShowLevelInList(INT16 mapnum, UINT8 gt)
return false; // not unlocked
// Check for TOL
if (!(mapheaderinfo[mapnum]->typeoflevel & tolflag))
if (!(mapheaderinfo[mapnum]->typeoflevel & tol))
return false;
// Should the map be hidden?
@ -3370,36 +3370,85 @@ boolean M_CanShowLevelInList(INT16 mapnum, UINT8 gt)
if (levellist.timeattack && (mapheaderinfo[mapnum]->menuflags & LF2_NOTIMEATTACK))
return false;
if (gametypedefaultrules[gt] & GTR_CAMPAIGN && levellist.selectedcup)
{
if (mapheaderinfo[mapnum]->cup != levellist.selectedcup)
return false;
}
// Don't permit cup when no cup requested (also no dupes in time attack)
if (levellist.cupmode && (levellist.timeattack || !cup) && mapheaderinfo[mapnum]->cup != cup)
return false;
// Survived our checks.
return true;
}
INT16 M_CountLevelsToShowInList(UINT8 gt)
UINT16 M_CountLevelsToShowInList(UINT32 tol, cupheader_t *cup)
{
INT16 mapnum, count = 0;
INT16 i, count = 0;
for (mapnum = 0; mapnum < nummapheaders; mapnum++)
if (M_CanShowLevelInList(mapnum, gt))
if (cup)
{
for (i = 0; i < CUPCACHE_MAX; i++)
{
if (!M_CanShowLevelInList(cup->cachedlevels[i], tol, cup))
continue;
count++;
}
return count;
}
for (i = 0; i < nummapheaders; i++)
if (M_CanShowLevelInList(i, tol, NULL))
count++;
return count;
}
INT16 M_GetFirstLevelInList(UINT8 gt)
UINT16 M_GetFirstLevelInList(UINT8 *i, UINT32 tol, cupheader_t *cup)
{
INT16 mapnum;
INT16 mapnum = NEXTMAP_INVALID;
for (mapnum = 0; mapnum < nummapheaders; mapnum++)
if (M_CanShowLevelInList(mapnum, gt))
return mapnum;
if (cup)
{
*i = 0;
mapnum = NEXTMAP_INVALID;
for (; *i < CUPCACHE_MAX; (*i)++)
{
if (!M_CanShowLevelInList(cup->cachedlevels[*i], tol, cup))
continue;
mapnum = cup->cachedlevels[*i];
break;
}
}
else
{
for (mapnum = 0; mapnum < nummapheaders; mapnum++)
if (M_CanShowLevelInList(mapnum, tol, NULL))
break;
}
return 0;
return mapnum;
}
UINT16 M_GetNextLevelInList(UINT16 map, UINT8 *i, UINT32 tol, cupheader_t *cup)
{
if (cup)
{
map = NEXTMAP_INVALID;
(*i)++;
for (; *i < CUPCACHE_MAX; (*i)++)
{
if (!M_CanShowLevelInList(cup->cachedlevels[*i], tol, cup))
continue;
map = cup->cachedlevels[*i];
break;
}
}
else
{
map++;
while (!M_CanShowLevelInList(map, tol, NULL) && map < nummapheaders)
map++;
}
return map;
}
struct cupgrid_s cupgrid;
@ -3407,7 +3456,7 @@ struct levellist_s levellist;
static void M_LevelSelectScrollDest(void)
{
UINT16 m = M_CountLevelsToShowInList(levellist.newgametype)-1;
UINT16 m = M_CountLevelsToShowInList(levellist.typeoflevel, levellist.selectedcup)-1;
levellist.dest = (6*levellist.cursor);
@ -3421,26 +3470,77 @@ static void M_LevelSelectScrollDest(void)
// Builds the level list we'll be using from the gametype we're choosing and send us to the apropriate menu.
static void M_LevelListFromGametype(INT16 gt)
{
levellist.newgametype = gt;
static boolean first = true;
if (first || gt != levellist.newgametype)
{
levellist.newgametype = gt;
levellist.typeoflevel = G_TOLFlag(gt);
levellist.cupmode = (!(gametypedefaultrules[gt] & GTR_NOCUPSELECT));
levellist.selectedcup = NULL;
first = false;
}
PLAY_CupSelectDef.prevMenu = currentMenu;
// Obviously go to Cup Select in gametypes that have cups.
// Use a really long level select in gametypes that don't use cups.
if (levellist.newgametype == GT_RACE)
if (levellist.cupmode)
{
cupheader_t *cup = kartcupheaders;
UINT8 highestid = 0;
size_t currentid = 0, highestunlockedid = 0;
const size_t unitlen = sizeof(cupheader_t*) * (CUPMENU_COLUMNS * CUPMENU_ROWS);
// Make sure there's valid cups before going to this menu.
if (cup == NULL)
I_Error("Can you really call this a racing game, I didn't recieve any Cups on my pillow or anything");
if (!cupgrid.builtgrid)
{
cupgrid.cappages = 2;
cupgrid.builtgrid = Z_Calloc(
cupgrid.cappages * unitlen,
PU_STATIC,
cupgrid.builtgrid);
if (!cupgrid.builtgrid)
{
I_Error("M_LevelListFromGametype: Not enough memory to allocate builtgrid");
}
}
memset(cupgrid.builtgrid, 0, cupgrid.cappages * unitlen);
while (cup)
{
if (!M_CountLevelsToShowInList(levellist.typeoflevel, cup))
{
// No valid maps, skip.
cup = cup->next;
continue;
}
if ((currentid * sizeof(cupheader_t*)) >= cupgrid.cappages * unitlen)
{
// Double the size of the buffer, and clear the other stuff.
const size_t firstlen = cupgrid.cappages * unitlen;
cupgrid.builtgrid = Z_Realloc(cupgrid.builtgrid,
firstlen * 2,
PU_STATIC, NULL);
if (!cupgrid.builtgrid)
{
I_Error("M_LevelListFromGametype: Not enough memory to reallocate builtgrid");
}
memset(cupgrid.builtgrid + firstlen, 0, firstlen);
cupgrid.cappages *= 2;
}
cupgrid.builtgrid[currentid] = cup;
if (cup->unlockrequired == -1 || unlockables[cup->unlockrequired].unlocked)
{
highestid = cup->id;
highestunlockedid = currentid;
if (Playing() && mapheaderinfo[gamemap-1] && mapheaderinfo[gamemap-1]->cup == cup)
{
cupgrid.x = cup->id % CUPMENU_COLUMNS;
@ -3448,10 +3548,16 @@ static void M_LevelListFromGametype(INT16 gt)
cupgrid.pageno = cup->id / (CUPMENU_COLUMNS * CUPMENU_ROWS);
}
}
currentid++;
cup = cup->next;
}
cupgrid.numpages = (highestid / (CUPMENU_COLUMNS * CUPMENU_ROWS)) + 1;
cupgrid.numpages = (highestunlockedid / (CUPMENU_COLUMNS * CUPMENU_ROWS)) + 1;
if (cupgrid.pageno >= cupgrid.numpages)
{
cupgrid.pageno = 0;
}
PLAY_LevelSelectDef.prevMenu = &PLAY_CupSelectDef;
M_SetupNextMenu(&PLAY_CupSelectDef, false);
@ -3504,25 +3610,15 @@ void M_LevelSelectInit(INT32 choice)
return;
}
levellist.newgametype = currentMenu->menuitems[itemOn].mvar2;
M_LevelListFromGametype(levellist.newgametype);
M_LevelListFromGametype(currentMenu->menuitems[itemOn].mvar2);
}
void M_CupSelectHandler(INT32 choice)
{
cupheader_t *newcup = kartcupheaders;
const UINT8 pid = 0;
(void)choice;
while (newcup)
{
if (newcup->id == CUPMENU_CURSORID)
break;
newcup = newcup->next;
}
if (menucmd[pid].dpad_lr > 0)
{
cupgrid.x++;
@ -3542,9 +3638,10 @@ void M_CupSelectHandler(INT32 choice)
if (cupgrid.x < 0)
{
cupgrid.x = CUPMENU_COLUMNS-1;
cupgrid.pageno--;
if (cupgrid.pageno < 0)
if (cupgrid.pageno == 0)
cupgrid.pageno = cupgrid.numpages-1;
else
cupgrid.pageno--;
}
S_StartSound(NULL, sfx_s3k5b);
M_SetMenuDelay(pid);
@ -3569,6 +3666,8 @@ void M_CupSelectHandler(INT32 choice)
if (M_MenuConfirmPressed(pid) /*|| M_MenuButtonPressed(pid, MBT_START)*/)
{
cupheader_t *newcup = cupgrid.builtgrid[CUPMENU_CURSORID];
M_SetMenuDelay(pid);
if ((!newcup)
@ -3640,7 +3739,7 @@ void M_CupSelectHandler(INT32 choice)
else
{
// Keep cursor position if you select the same cup again, reset if it's a different cup
if (!levellist.selectedcup || newcup->id != levellist.selectedcup->id)
if (levellist.selectedcup != newcup)
{
levellist.cursor = 0;
levellist.selectedcup = newcup;
@ -3671,8 +3770,7 @@ void M_CupSelectTick(void)
void M_LevelSelectHandler(INT32 choice)
{
INT16 start = M_GetFirstLevelInList(levellist.newgametype);
INT16 maxlevels = M_CountLevelsToShowInList(levellist.newgametype);
INT16 maxlevels = M_CountLevelsToShowInList(levellist.typeoflevel, levellist.selectedcup);
const UINT8 pid = 0;
(void)choice;
@ -3703,20 +3801,20 @@ void M_LevelSelectHandler(INT32 choice)
if (M_MenuConfirmPressed(pid) /*|| M_MenuButtonPressed(pid, MBT_START)*/)
{
INT16 map = start;
UINT8 i = 0;
INT16 map = M_GetFirstLevelInList(&i, levellist.typeoflevel, levellist.selectedcup);
INT16 add = levellist.cursor;
M_SetMenuDelay(pid);
while (add > 0)
{
map++;
while (!M_CanShowLevelInList(map, levellist.newgametype) && map < nummapheaders)
map++;
map = M_GetNextLevelInList(map, &i, levellist.typeoflevel, levellist.selectedcup);
if (map >= nummapheaders)
{
break;
}
add--;
}

View file

@ -7795,13 +7795,9 @@ UINT8 P_InitMapData(boolean existingmapheaders)
if (strcasecmp(cup->levellist[j], name) != 0)
continue;
// Only panic about back-reference for non-bonus material.
if (j < MAXLEVELLIST)
{
if (mapheaderinfo[i]->cup)
I_Error("P_InitMapData: Map %s cannot appear in cups multiple times! (First in %s, now in %s)", name, mapheaderinfo[i]->cup->name, cup->name);
// Have a map recognise the first cup it's a part of.
if (!mapheaderinfo[i]->cup)
mapheaderinfo[i]->cup = cup;
}
cup->cachedlevels[j] = i;
}