diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 000000000..7a89862eb --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,16 @@ +{ + "files.associations": { + "charconv": "c", + "chrono": "c", + "optional": "c", + "format": "c", + "ranges": "c", + "ratio": "c", + "system_error": "c", + "array": "c", + "functional": "c", + "tuple": "c", + "type_traits": "c", + "utility": "c" + } +} \ No newline at end of file diff --git a/src/deh_tables.c b/src/deh_tables.c index 11e8343c3..c0701085f 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -755,6 +755,8 @@ struct menu_routine_s const MENU_ROUTINES[] = { { "QUITVIEWSERVER", &MR_QuitViewServer }, { "HANDLEVIEWSERVER", &MR_HandleViewServer }, { "CAMERASETUP", &MR_CameraSetup }, + // { "BARCSS", &MR_HandleBarCss }, + { "HANDLESETUPMULTIPLAYERMENU", &MR_HandleSetupMultiPlayerMenu }, #ifdef HAVE_DISCORDRPC { "HANDLEDISCORDREQUESTS", &MR_HandleDiscordRequests }, #endif @@ -769,6 +771,9 @@ struct menu_drawer_s const MENU_DRAWERS[] = { { "DRAWTIMEATTACKMENU", &MD_DrawTimeAttackMenu }, { "DRAWMPMAINMENU", &MD_DrawMPMainMenu }, { "DRAWSETUPMULTIPLAYERMENU", &MD_DrawSetupMultiPlayerMenu }, + // { "DRAWCSSCHARACTER", &MD_DrawCssCharacter }, + // { "DRAWBARCSSSELECTOR", &MD_DrawBarCssSelector }, + // { "DRAWGRIDCSSSELECTOR", &MD_DrawGridCssSelector }, { "DRAWVIDEOMODE", &MD_DrawVideoMode }, { "DRAWADDONS", &MD_DrawAddons }, { "DRAWREPLAYSTARTMENU", &MD_DrawReplayStartMenu }, diff --git a/src/doomdef.h b/src/doomdef.h index f1e9a57fd..8355f8528 100644 --- a/src/doomdef.h +++ b/src/doomdef.h @@ -103,7 +103,7 @@ extern "C" { // Special Hashing. //#define NOFILEHASH -//#define NOVERIFYIWADS +#define NOVERIFYIWADS // Uncheck this to compile debugging code //#define RANGECHECK diff --git a/src/m_menu.c b/src/m_menu.c index c4084cbcd..af085e973 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -379,6 +379,21 @@ static INT16 lastnextmap = 1; static CV_PossibleValue_t skins_cons_t[] = {{0, "MIN"}, {MAXSKINS, "MAX"}, {0, NULL}}; consvar_t cv_chooseskin = CVAR_INIT ("chooseskin", "0", CV_HIDEN|CV_CALL|CV_NOINIT, skins_cons_t, Nextmap_OnChange); +static CV_PossibleValue_t skinselectstyle_cons_t[] = {{0, "Bar"}, {1, "Grid"}, {0, NULL}}; +consvar_t cv_skinselectstyle = CVAR_INIT ("skinselectstyle", "Grid", CV_SAVE|CV_CALL|CV_NOINIT, skinselectstyle_cons_t, Skinselectstyle_option_Onchange); + +static CV_PossibleValue_t skinselectsort_cons_t[] = { + {SKINMENUSORT_ID, "Order Added"}, + {SKINMENUSORT_NAME, "Name"}, + {SKINMENUSORT_SPEED, "Speed"}, + {SKINMENUSORT_WEIGHT, "Weight"}, + {SKINMENUSORT_ACCEL, "Accel"}, + {SKINMENUSORT_HANDLING, "Handling"}, + {SKINMENUSORT_PREFCOLOR, "Preferred Colour"}, + {0, NULL} +}; +consvar_t cv_skinselectsort = CVAR_INIT ("skinselectsort", "Order Added", CV_SAVE|CV_CALL|CV_NOINIT, skinselectsort_cons_t, Skinsort_option_Onchange); + // This gametype list is integral for many different reasons. // When you add gametypes here, don't forget to update them in dehacked.c and doomstat.h! CV_PossibleValue_t gametype_cons_t[NUMGAMETYPES+1]; @@ -819,6 +834,42 @@ void Moviemode_option_Onchange(void) ; } + +//width now 9 so we can make moe's "2D" grid just an option in the normal grid +#define SKINGRIDWIDTH 9 +#define SKINGRIDHEIGHT 6 + +static INT32 gridcss_skinydrag; +static INT32 gridcss_skinmemory; +static INT32 gridcss_row; +static INT32 gridcss_column; + +void Skinsort_option_Onchange(void) +{ + SortSkins(); + INT32 sortedIndex = FindSortedSkinIndex(cv_chooseskin.value); + gridcss_row = sortedIndex % SKINGRIDWIDTH; + gridcss_column = sortedIndex / SKINGRIDWIDTH; + // I really need to macro this lol + gridcss_skinydrag = CLAMP(gridcss_column - SKINGRIDHEIGHT + 1, 0, ((numskins - 1) / SKINGRIDWIDTH) - SKINGRIDHEIGHT + 1); +} + +void Skinselectstyle_option_Onchange(void) +{ + boolean visible = cv_skinselectstyle.value == 0; + M_SetItemVisible(MN_MP_PLAYERSETUP, "ACCELERATION", visible); + M_SetItemVisible(MN_MP_PLAYERSETUP, "MAXSPEED", visible); + M_SetItemVisible(MN_MP_PLAYERSETUP, "HANDLING", visible); + M_SetItemVisible(MN_MP_PLAYERSETUP, "WEIGHT", visible); + M_SetItemVisible(MN_MP_PLAYERSETUP, "ARROWLR", visible); + M_SetItemVisible(MN_MP_PLAYERSETUP, "ARROWUD", visible); + M_SetItemVisible(MN_MP_PLAYERSETUP, "STATBG", visible); + + M_SetItemVisible(MN_MP_PLAYERSETUP, "STATBAR", !visible); + + Skinsort_option_Onchange(); +} + // ========================================================================== // END ORGANIZATION STUFF. // ========================================================================== @@ -2080,6 +2131,8 @@ void M_Init(void) CV_RegisterVar(&cv_nextmap); CV_RegisterVar(&cv_newgametype); CV_RegisterVar(&cv_chooseskin); + CV_RegisterVar(&cv_skinselectstyle); + CV_RegisterVar(&cv_skinselectsort); CV_RegisterVar(&cv_autorecord); if (dedicated) @@ -6730,31 +6783,47 @@ static state_t *follower_state; // for either player 1 or 2 static UINT8 setupplayer; +#define charw 72 + void MD_DrawSetupMultiPlayerMenu(void) +{ + // use generic drawer for cursor, items and title + // bg, text, arrows handled by generic drawer + MD_DrawGenericMenu(); + + MD_DrawCssColourBar(); + MD_DrawCssCharacter(); + + if (cv_skinselectstyle.value) + { + MD_DrawCssStatBars(); + MD_DrawGridCssSelector(); + } + else + { + MD_DrawCssStatBacker(); + MD_DrawBarCssSelector(); + } +} + +void MD_DrawCssStatBacker(void) { - INT32 mx, my, st, flags = 0; - spritedef_t *sprdef; - spriteframe_t *sprframe; + INT32 mx, my; patch_t *statdot = W_CachePatchName("K_SDOT0", PU_CACHE); - patch_t *patch; - UINT8 frame; UINT8 speed; UINT8 weight; const UINT8 *flashcol = V_GetStringColormap(highlightflags); INT16 i; + INT32 skintodisplay = cv_chooseskin.value; + mx = menudefs[MN_MP_PLAYERSETUP].x; my = menudefs[MN_MP_PLAYERSETUP].y; - // use generic drawer for cursor, items and title - MD_DrawGenericMenu(); - // SRB2Kart: draw the stat backer - // bg, text, arrows handled by generic drawer - for (i = 0; i < numskins; i++) // draw the stat dots { - if (i != cv_chooseskin.value && R_SkinAvailable(skins[i].name) != -1) + if (i != skintodisplay && R_SkinAvailable(skins[i].name) != -1) { speed = skins[i].kartspeed; weight = skins[i].kartweight; @@ -6762,8 +6831,8 @@ void MD_DrawSetupMultiPlayerMenu(void) } } - speed = skins[cv_chooseskin.value].kartspeed; - weight = skins[cv_chooseskin.value].kartweight; + speed = skins[skintodisplay].kartspeed; + weight = skins[skintodisplay].kartweight; statdot = W_CachePatchName("K_SDOT1", PU_CACHE); if (skullAnimCounter < 4) // SRB2Kart: we draw this dot later so that it's not covered if there's multiple skins with the same stats @@ -6773,9 +6842,52 @@ void MD_DrawSetupMultiPlayerMenu(void) statdot = W_CachePatchName("K_SDOT2", PU_CACHE); // coloured center V_DrawFixedPatch(((BASEVIDWIDTH - mx - 80) + ((speed-1)*8))< 7 * FRACUNIT; cursorframe -= 7 * FRACUNIT) {} + INT32 skintodisplay = cv_chooseskin.value; - cursor = W_CachePatchName(va("K_BHILI%d", (cursorframe >> FRACBITS) + 1), PU_CACHE); - - if (col < 0) - col += numskins; - while (k <= icons) - { - if (!(k++)) - { - scale = FRACUNIT; - face = faceprefix[col][FACE_WANTED]; - offx = 12; - offy = 0; - } - else - { - scale = FRACUNIT/2; - face = faceprefix[col][FACE_RANK]; - offx = 8; - offy = 8; - } - colmap = R_GetTranslationColormap(col, cv_dummycolor.value, GTC_MENUCACHE); - if (face) - V_DrawFixedPatch((x+offx)<= numskins) - col -= numskins; - x += FixedMul(iconwidth<numframes) // No frames ?? return; // Can't render! @@ -6901,10 +6979,10 @@ void MD_DrawSetupMultiPlayerMenu(void) V_DrawFill(mx + 43 - (charw/2), my+65, charw, 84, 159); // draw player sprite - UINT8 *colormap = R_GetTranslationColormap(cv_chooseskin.value, cv_dummycolor.value, GTC_MENUCACHE); + UINT8 *colormap = R_GetTranslationColormap(skintodisplay, cv_dummycolor.value, GTC_MENUCACHE); V_DrawFixedPatch((mx+43)<>ANGLETOFINESHIFT) & FINEMASK)); - UINT16 color = K_GetEffectiveFollowerColor(cv_followercolor[setupplayer].value, &fl, cv_dummycolor.value, &skins[cv_chooseskin.value]); + UINT16 color = K_GetEffectiveFollowerColor(cv_followercolor[setupplayer].value, &fl, cv_dummycolor.value, &skins[skintodisplay]); colormap = R_GetTranslationColormap(TC_DEFAULT, color, 0); // why does GTC_MENUCACHE not work here...? INT32 x = (mx+65)*FRACUNIT; @@ -6971,8 +7049,350 @@ void MD_DrawSetupMultiPlayerMenu(void) Z_Free(colormap); } } - +} #undef charw + +void MD_DrawBarCssSelector(void) +{ + INT32 my; + + // mx = menudefs[MN_MP_PLAYERSETUP].x; + my = menudefs[MN_MP_PLAYERSETUP].y; + + // character bar, ripped off the color bar :V +#define iconwidth 32 + { + const INT32 icons = 4; + INT32 k = -icons; + INT16 col = (cv_chooseskin.value - icons) % numskins; + INT32 x = BASEVIDWIDTH/2 - ((icons+1)*24) - 4; + fixed_t scale = FRACUNIT/2; + INT32 offx = 8, offy = 8; + patch_t *cursor; + static fixed_t cursorframe = 0; + patch_t *face; + UINT8 *colmap; + + cursorframe += renderdeltatics / 4; + for (; cursorframe > 7 * FRACUNIT; cursorframe -= 7 * FRACUNIT) {} + + cursor = W_CachePatchName(va("K_BHILI%d", (cursorframe >> FRACBITS) + 1), PU_CACHE); + + if (col < 0) + col += numskins; + while (k <= icons) + { + if (!(k++)) + { + scale = FRACUNIT; + face = faceprefix[col][FACE_WANTED]; + offx = 12; + offy = 0; + } + else + { + scale = FRACUNIT/2; + face = faceprefix[col][FACE_RANK]; + offx = 8; + offy = 8; + } + colmap = R_GetTranslationColormap(col, cv_dummycolor.value, GTC_MENUCACHE); + if (face) + V_DrawFixedPatch((x+offx)<= numskins) + col -= numskins; + x += FixedMul(iconwidth< SKINGRIDHEIGHT) + { + //draw scroll bar "back" + x += dx + 4; + V_DrawFill(x, y, scrx+2, dy, 9); + V_DrawFill(x, y, 1, dy, 10); + V_DrawFill(x, y + dy - 1, scrx+2, 1, 10); + x++; + y++; + dy -= 2; + V_DrawFill(x, y, scrx, dy, 15); + + //draw scroll bar bar + barlen = dy * SKINGRIDHEIGHT / columncount; + barpos = dy * gridcss_skinydrag / columncount; + + if (gridcss_skinydrag >= columncount - SKINGRIDHEIGHT) + { + barpos = dy - barlen; + } + + V_DrawFill(x, y + barpos, scrx, barlen, 5); + V_DrawFill(x, y + barpos, 1, barlen, 7); + V_DrawFill(x, y + barpos + barlen - 1, scrx, 1, 7); + } + } + // end background and scroll bar + + for (INT32 gridslot = 0; gridslot < SKINGRIDWIDTH*SKINGRIDHEIGHT; gridslot++) + { + gridx = ((gridslot % SKINGRIDWIDTH) * 18) + ((BASEVIDWIDTH / 2) - (18 * SKINGRIDWIDTH) - 8) + 100 + SKINXSHIFT; //BASEVIDWIDTH / 2 - ((icons + 1) * 24) - 4; + gridy = ((gridslot / SKINGRIDWIDTH) * 18) + ((BASEVIDHEIGHT / 2) - (18 * (SKINGRIDWIDTH/2))) + SKINYSHIFT; //BASEVIDWIDTH / 2 - ((icons + 1) * 24) - 4; + calcs = gridslot + (gridcss_skinydrag * SKINGRIDWIDTH); + + if (calcs < numskins) + skinn = skinsorted[calcs]; + // skinn = calcs; + else if (gridslot % SKINGRIDWIDTH == 0) + break; //really conveniant (sic) place to break out here + else + { + // draw an empty slot + V_DrawFill(gridx, gridy, 16, 16, 158); + continue; + } + + if (skinn == skintodisplay) + { + cursorx = gridx; + cursory = gridy; + } + else + { + face = faceprefix[skinn][FACE_RANK]; + colmap = R_GetTranslationColormap(skinn, skins[skinn].prefcolor, GTC_MENUCACHE); + V_DrawFixedPatch(gridx * FRACUNIT, gridy * FRACUNIT, FRACUNIT, 0, face, colmap); + } + } + + // draw wanted portrait and cursor + if (M_IsItemOn(MN_MP_PLAYERSETUP, "SKIN")) + { + face = faceprefix[skintodisplay][FACE_WANTED]; + colmap = R_GetTranslationColormap(skintodisplay, cv_dummycolor.value, GTC_MENUCACHE); + V_DrawFixedPatch((cursorx * FRACUNIT) - (face->width * FRACUNIT/4), (cursory * FRACUNIT) - (face->height * FRACUNIT/4), FRACUNIT, 0, face, colmap); + + + cursorframe += renderdeltatics / 4; + for (; cursorframe > 7 * FRACUNIT; cursorframe -= 7 * FRACUNIT) {} + cursor = W_CachePatchName(va("K_BHILI%d", (cursorframe >> FRACBITS) + 1), PU_CACHE); + // cursor patch offsets are wrong so draw at same coordinate as portrait + V_DrawFixedPatch((cursorx * FRACUNIT) - (face->width * FRACUNIT/4), (cursory * FRACUNIT) - (face->height * FRACUNIT/4), FRACUNIT, 0, cursor, colmap); + + } + else + { + face = faceprefix[skintodisplay][FACE_RANK]; + colmap = R_GetTranslationColormap(skintodisplay, cv_dummycolor.value, GTC_MENUCACHE); + V_DrawFixedPatch(cursorx * FRACUNIT, cursory * FRACUNIT, FRACUNIT, 0, face, colmap); + cursor = W_CachePatchName("M_FSEL", PU_CACHE); + + if (skullAnimCounter < 4) + { + colmap = V_GetStringColormap(skincolors[cv_dummycolor.value].chatcolor); + V_DrawFixedPatch(cursorx * FRACUNIT, cursory * FRACUNIT, FRACUNIT/2, 0, cursor, colmap); + } + } +} + +// +// Maps a CSS grid selection cursor column (x) and row (y) to a skin selection index +// +INT32 MapGridSelectToSkin(INT32 row, INT32 column) +{ + return (row % SKINGRIDWIDTH) + (column * SKINGRIDWIDTH); +} + +INT32 MR_HandleSetupMultiPlayerMenu(INT32 choice) +{ + INT32 sortedIndex; + // don't consume input if we're not interacting with the grid CSS + if (!(M_IsItemOn(MN_MP_PLAYERSETUP, "SKIN") && cv_skinselectstyle.value)) + { + return false; + } + + switch (choice) + { + case KEY_DOWNARROW: + if (gridcss_column == (numskins - 1) / SKINGRIDWIDTH) + { + M_SetItemOn(MN_MP_PLAYERSETUP, "FOLLOWER"); + CV_SetValue(&cv_chooseskin, gridcss_skinmemory); + sortedIndex = FindSortedSkinIndex(cv_chooseskin.value); + gridcss_row = sortedIndex % SKINGRIDWIDTH; + gridcss_column = sortedIndex / SKINGRIDWIDTH; + + gridcss_skinydrag = CLAMP(gridcss_column - SKINGRIDHEIGHT + 1, 0, ((numskins - 1) / SKINGRIDWIDTH) - SKINGRIDHEIGHT - 1); + } + else + { + gridcss_column++; + + if (MapGridSelectToSkin(gridcss_row, gridcss_column) > (numskins - 1)) + { + gridcss_row = (numskins - 1) % SKINGRIDWIDTH; + gridcss_column = (numskins - 1) / SKINGRIDWIDTH; + } + + if ((gridcss_column - gridcss_skinydrag) > SKINGRIDHEIGHT - 1) + gridcss_skinydrag++; + } + S_StartSound(NULL, sfx_menu1); + break; + case KEY_UPARROW: + if (gridcss_column == 0) + { + M_SetItemOn(MN_MP_PLAYERSETUP, "NAME"); + CV_SetValue(&cv_chooseskin, gridcss_skinmemory); + sortedIndex = FindSortedSkinIndex(cv_chooseskin.value); + gridcss_row = sortedIndex % SKINGRIDWIDTH; + gridcss_column = sortedIndex / SKINGRIDWIDTH; + + gridcss_skinydrag = CLAMP(gridcss_column - SKINGRIDHEIGHT + 1, 0, ((numskins - 1) / SKINGRIDWIDTH) - SKINGRIDHEIGHT - 1); + } + else + { + gridcss_column--; + + if (gridcss_column < gridcss_skinydrag) + gridcss_skinydrag--; + } + S_StartSound(NULL, sfx_menu1); + break; + case KEY_LEFTARROW: + if (gridcss_row == 0) + { + if (MapGridSelectToSkin(SKINGRIDWIDTH - 1, gridcss_column) > (numskins-1)) + gridcss_row = numskins - (gridcss_column * SKINGRIDWIDTH) - 1; + else + gridcss_row = SKINGRIDWIDTH - 1; + if (gridcss_column > 0) + { + gridcss_row = SKINGRIDWIDTH - 1; + gridcss_column--; + + if (gridcss_column < gridcss_skinydrag) + gridcss_skinydrag--; + } + } + else + { + gridcss_row--; + } + S_StartSound(NULL, sfx_menu1); + break; + case KEY_RIGHTARROW: + if ((gridcss_row + 1 == SKINGRIDWIDTH) || (MapGridSelectToSkin(gridcss_row + 1, gridcss_column) > (numskins - 1))) + { + gridcss_row = 0; + if (MapGridSelectToSkin(gridcss_row, gridcss_column + 1) <= (numskins - 1)) + { + gridcss_column++; + + if ((gridcss_column - gridcss_skinydrag) > SKINGRIDHEIGHT - 1) + gridcss_skinydrag++; + } + } + else + { + gridcss_row++; + } + S_StartSound(NULL, sfx_menu1); + break; + case KEY_PGDN: + if (gridcss_column <= (numskins - 1) / SKINGRIDWIDTH) + { + gridcss_column = min(gridcss_column + SKINGRIDHEIGHT - 1, (numskins - 1) / SKINGRIDWIDTH); + + if (MapGridSelectToSkin(gridcss_row, gridcss_column) > (numskins - 1)) + { + gridcss_row = (numskins - 1) % SKINGRIDWIDTH; + gridcss_column = (numskins - 1) / SKINGRIDWIDTH; + } + + gridcss_skinydrag = CLAMP(gridcss_column - SKINGRIDHEIGHT + 1, 0, ((numskins - 1) / SKINGRIDWIDTH) - SKINGRIDHEIGHT + 1); + + S_StartSound(NULL, sfx_menu1); + } + break; + case KEY_PGUP: + if (gridcss_column > 0) + { + gridcss_column = max(gridcss_column - SKINGRIDHEIGHT + 1, 0); + gridcss_skinydrag = max(gridcss_column, 0); + S_StartSound(NULL, sfx_menu1); + } + break; + case KEY_TAB: + M_SetItemOn(MN_MP_PLAYERSETUP, "FOLLOWER"); + CV_SetValue(&cv_chooseskin, gridcss_skinmemory); + sortedIndex = FindSortedSkinIndex(cv_chooseskin.value); + gridcss_row = sortedIndex % SKINGRIDWIDTH; + gridcss_column = sortedIndex / SKINGRIDWIDTH; + + gridcss_skinydrag = CLAMP(gridcss_column - SKINGRIDHEIGHT + 1, 0, ((numskins - 1) / SKINGRIDWIDTH) - SKINGRIDHEIGHT - 1); + + S_StartSound(NULL, sfx_menu1); + break; + case KEY_ENTER: + if (cv_chooseskin.value < numskins) + { + gridcss_skinmemory = cv_chooseskin.value; + S_StartSound(NULL, sfx_s221); + } + break; + default: + return false; + } + + CV_SetValue(&cv_chooseskin, skinsorted[MapGridSelectToSkin(gridcss_row, gridcss_column)]); + return true; } // follower state update. This is its own function so that it's at least somewhat clean @@ -7008,14 +7428,18 @@ INT32 MR_SetupMultiPlayer(INT32 arg) multi_tics = multi_state->tics * FRACUNIT; CV_Set(&cv_dummyname, cv_playername[arg].string); - CV_SetValue(&cv_dummyfollower, cv_follower[arg].value); CV_SetValue(&cv_chooseskin, R_SkinAvailable(cv_skin[arg].string)); + gridcss_skinmemory = cv_chooseskin.value; + CV_SetValue(&cv_dummyfollower, cv_follower[arg].value); CV_SetValue(&cv_dummycolor, cv_playercolor[arg].value); + + Skinsort_option_Onchange(); M_GetFollowerState(); // update follower state // disable skin changes if we can't actually change skins M_SetItemDisabled(MN_MP_PLAYERSETUP, "SKIN", splitscreen >= arg && !CanChangeSkin(arg == 0 ? consoleplayer : g_localplayers[arg])); + Skinselectstyle_option_Onchange(); return true; } @@ -7023,6 +7447,10 @@ INT32 MR_SetupMultiPlayer(INT32 arg) INT32 MR_QuitMultiPlayerMenu(INT32 choice) { (void)choice; + + if (cv_skinselectstyle.value == 1) + CV_SetValue(&cv_chooseskin, gridcss_skinmemory); + const char *followername = cv_dummyfollower.value == -1 ? "None" : followers[cv_dummyfollower.value].skinname; COM_BufInsertText(va( @@ -7034,6 +7462,11 @@ INT32 MR_QuitMultiPlayerMenu(INT32 choice) )); return true; } +#undef SKINXSHIFT +#undef SKINYSHIFT + +#undef SKINGRIDWIDTH +#undef SKINGRIDHEIGH void M_AddMenuColor(UINT16 color) { menucolor_t *c; diff --git a/src/m_menu.h b/src/m_menu.h index eebc36c86..b3f2b8cc5 100644 --- a/src/m_menu.h +++ b/src/m_menu.h @@ -302,6 +302,7 @@ INT32 MR_Retry(INT32 choice); INT32 MR_EndGame(INT32 choice); INT32 MR_MapChange(INT32 choice); INT32 MR_SetupMultiPlayer(INT32 choice); +INT32 MR_HandleSetupMultiPlayerMenu(INT32 choice); INT32 MR_ConfirmSpectate(INT32 choice); INT32 MR_ConfirmEnterGame(INT32 choice); INT32 MR_ConfirmTeamScramble(INT32 choice); @@ -339,6 +340,12 @@ void MD_DrawReplayHut(void); void MD_DrawTimeAttackMenu(void); void MD_DrawMPMainMenu(void); void MD_DrawSetupMultiPlayerMenu(void); +void MD_DrawCssStatBacker(void); +void MD_DrawCssStatBars(void); +void MD_DrawCssColourBar(void); +void MD_DrawCssCharacter(void); +void MD_DrawBarCssSelector(void); +void MD_DrawGridCssSelector(void); void MD_DrawConnectMenu(void); void MD_DrawVideoMode(void); void MD_DrawAddons(void); @@ -353,6 +360,8 @@ void MD_DrawViewServer(void); void MD_DrawDiscordRequests(void); #endif +INT32 MapGridSelectToSkin(INT32 column, INT32 row); + // Maybe this goes here????? Who knows. boolean M_MouseNeeded(void); @@ -461,6 +470,9 @@ fixed_t M_GetMapThumbnail(INT16 mapnum, patch_t **out); // Moviemode menu updating void Moviemode_option_Onchange(void); +void Skinsort_option_Onchange(void); +void Skinselectstyle_option_Onchange(void); + // Player Setup menu colors linked list struct menucolor_t { menucolor_t *next; diff --git a/src/r_skins.c b/src/r_skins.c index 0a719fb7f..2fb590a1b 100644 --- a/src/r_skins.c +++ b/src/r_skins.c @@ -37,6 +37,7 @@ INT32 numskins = 0; skin_t skins[MAXSKINS]; +INT32 skinsorted[MAXSKINS]; // FIXTHIS: don't work because it must be inistilised before the config load //#define SKINVALUES @@ -214,6 +215,8 @@ void R_InitSkins(void) } #endif + // doesn't seem right but it's what the original did + memset(skinsorted, 0, sizeof(skinsorted)); // yes default skin! numskins = 1; @@ -246,6 +249,140 @@ UINT32 R_GetSkinAvailabilities(void) return response; } +// +// From callmore's skin select +// Sort function(s) for sorting skin names +// +static int CompareSkinIds(const void *a, const void *b) +{ + const INT32 val_a = *((const INT32 *)a); + const INT32 val_b = *((const INT32 *)b); + if (val_a > val_b) + return 1; + else if (val_a < val_b) + return -1; + else + return 0; +} + +static int CompareSkinNames(const void *a, const void *b) +{ + const skin_t *in1 = &skins[*(const INT32 *)a]; + const skin_t *in2 = &skins[*(const INT32 *)b]; + return strcmp(in1->realname, in2->realname); +} + +static int CompareSkinSpeeds(const void *a, const void *b) +{ + const skin_t *in1 = &skins[*(const INT32 *)a]; + const skin_t *in2 = &skins[*(const INT32 *)b]; + INT32 temp = 0; + // check speed + if (in1->kartspeed < in2->kartspeed) + return -1; + else if (in2->kartspeed < in1->kartspeed) + return 1; + // then check weight + if (in1->kartweight < in2->kartweight) + return -1; + else if (in2->kartweight < in1->kartweight) + return 1; + // then check name + if ((temp = strcmp(in1->realname, in2->realname))) + return temp; + // sort by internal name + return strcmp(in1->name, in2->name); +} + +static int CompareSkinWeights(const void *a, const void *b) +{ + const skin_t *in1 = &skins[*(const INT32 *)a]; + const skin_t *in2 = &skins[*(const INT32 *)b]; + INT32 temp = 0; + // check weight + if (in1->kartweight < in2->kartweight) + return -1; + else if (in2->kartweight < in1->kartweight) + return 1; + // then check speed + if (in1->kartspeed < in2->kartspeed) + return -1; + else if (in2->kartspeed < in1->kartspeed) + return 1; + // then check name + if ((temp = strcmp(in1->realname, in2->realname))) + return temp; + // sort by internal name + return strcmp(in1->name, in2->name); +} + +static int CompareSkinAccels(const void *a, const void *b) +{ + return CompareSkinSpeeds(b, a); +} + +static int CompareSkinHandlings(const void *a, const void *b) +{ + return CompareSkinWeights(b, a); +} + +static int CompareSkinColours(const void *a, const void *b) +{ + const skin_t *in1 = &skins[*(const INT32 *)a]; + const skin_t *in2 = &skins[*(const INT32 *)b]; + INT32 temp = 0; + + // check prefcolor + if (in1->prefcolor < in2->prefcolor) + return -1; + else if (in2->prefcolor < in1->prefcolor) + return 1; + // then check name + if ((temp = strcmp(in1->realname, in2->realname))) + return temp; + // sort by internal name + return strcmp(in1->name, in2->name); +} + +void SortSkins(void) +{ + int (*_sortingFunc)(const void *, const void *); + switch (cv_skinselectsort.value) + { + case SKINMENUSORT_NAME: + _sortingFunc = CompareSkinNames; + break; + case SKINMENUSORT_SPEED: + _sortingFunc = CompareSkinSpeeds; + break; + case SKINMENUSORT_WEIGHT: + _sortingFunc = CompareSkinWeights; + break; + case SKINMENUSORT_ACCEL: + _sortingFunc = CompareSkinAccels; + break; + case SKINMENUSORT_HANDLING: + _sortingFunc = CompareSkinHandlings; + break; + case SKINMENUSORT_PREFCOLOR: + _sortingFunc = CompareSkinColours; + break; + default: + _sortingFunc = CompareSkinIds; + break; + } + qs22j(skinsorted, numskins, sizeof(INT32), _sortingFunc); +} + +INT32 FindSortedSkinIndex(INT32 skinnum) +{ + for (INT32 i = 0; i < numskins; i++) + { + if (skinsorted[i] == skinnum) return i; + } + return 0; +} + // returns true if available in circumstances, otherwise nope // warning don't use with an invalid skinnum other than -1 which always returns true boolean R_SkinUsable(INT32 playernum, INT32 skinnum) @@ -910,8 +1047,10 @@ next_token: HWR_AddPlayerModel(numskins); #endif + skinsorted[numskins] = numskins; numskins++; } + SortSkins(); return; } diff --git a/src/r_skins.h b/src/r_skins.h index c66456b4a..82669bc76 100644 --- a/src/r_skins.h +++ b/src/r_skins.h @@ -34,6 +34,8 @@ extern "C" { #define DEFAULTSKIN3 "knuckles" // third player #define DEFAULTSKIN4 "eggman" // fourth player +extern consvar_t cv_skinselectstyle, cv_skinselectsort; + /// The skin_t struct struct skin_t { @@ -76,9 +78,22 @@ enum facepatches { NUMFACES }; +enum skinmenusortoption +{ + SKINMENUSORT_ID = 0, + SKINMENUSORT_NAME, + SKINMENUSORT_SPEED, + SKINMENUSORT_WEIGHT, + SKINMENUSORT_ACCEL, + SKINMENUSORT_HANDLING, + SKINMENUSORT_PREFCOLOR, + NUMSKINMENUSORT, +}; + /// Externs extern INT32 numskins; extern skin_t skins[MAXSKINS]; +extern INT32 skinsorted[MAXSKINS]; extern CV_PossibleValue_t Forceskin_cons_t[]; @@ -86,6 +101,8 @@ extern const UINT8 kart2spr2[26][2]; /// Function prototypes void R_InitSkins(void); +void SortSkins(void); +INT32 FindSortedSkinIndex(INT32 skinnum); void SetPlayerSkin(INT32 playernum,const char *skinname); void SetPlayerSkinByNum(INT32 playernum,INT32 skinnum); // Tails 03-16-2002