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/m_menu.c b/src/m_menu.c index 55c5b4b7c..a542938c6 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -381,6 +381,17 @@ consvar_t cv_chooseskin = CVAR_INIT ("chooseskin", "0", CV_HIDEN|CV_CALL|CV_NOIN static CV_PossibleValue_t skinselectstyle_cons_t[] = {{0, "Bar"}, {1, "Grid"}, {0, NULL}}; consvar_t cv_skinselectstyle = CVAR_INIT ("skinselectstyle", "Grid", CV_SAVE, skinselectstyle_cons_t, NULL); +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"}, + {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]; @@ -821,6 +832,26 @@ 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; + + gridcss_skinydrag = CLAMP(gridcss_column - SKINGRIDHEIGHT + 1, 0, ((numskins - 1) / SKINGRIDWIDTH) - SKINGRIDHEIGHT - 1); +} + // ========================================================================== // END ORGANIZATION STUFF. // ========================================================================== @@ -2082,6 +2113,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) @@ -6723,11 +6756,6 @@ INT32 MR_ConnectIP(INT32 choice) static fixed_t multi_tics; static state_t *multi_state; -static INT32 gridcss_skinydrag; -static INT32 gridcss_skinmemory; -static INT32 gridcss_row; -static INT32 gridcss_column; - // used for follower display on player setup menu static fixed_t follower_tics; static UINT32 follower_frame; // used for FF_ANIMATE garbo @@ -7030,10 +7058,6 @@ void MD_DrawBarCssSelector(void) // minenice's grid style CSS from an unfinished moe mansion pull request // originally based on callmore's skin select -//width now 9 so we can make moe's "2D" grid just an option in the normal grid -#define SKINGRIDWIDTH 9 -#define SKINGRIDHEIGHT 6 - #define SKINXSHIFT 33 #define SKINYSHIFT 28 @@ -7105,8 +7129,8 @@ void MD_DrawGridCssSelector(void) calcs = gridslot + (gridcss_skinydrag * SKINGRIDWIDTH); if (calcs < numskins) - // skinn = skinsorted[calcs]; - skinn = calcs; + skinn = skinsorted[calcs]; + // skinn = calcs; else if (gridslot % SKINGRIDWIDTH == 0) break; //really conveniant (sic) place to break out here else @@ -7169,6 +7193,7 @@ INT32 MapGridSelectToSkin(INT32 row, INT32 column) 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)) { @@ -7182,8 +7207,9 @@ INT32 MR_HandleSetupMultiPlayerMenu(INT32 choice) { M_SetItemOn(MN_MP_PLAYERSETUP, "FOLLOWER"); CV_SetValue(&cv_chooseskin, gridcss_skinmemory); - gridcss_row = cv_chooseskin.value % SKINGRIDWIDTH; - gridcss_column = cv_chooseskin.value / SKINGRIDWIDTH; + 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); } @@ -7207,8 +7233,9 @@ INT32 MR_HandleSetupMultiPlayerMenu(INT32 choice) { M_SetItemOn(MN_MP_PLAYERSETUP, "NAME"); CV_SetValue(&cv_chooseskin, gridcss_skinmemory); - gridcss_row = cv_chooseskin.value % SKINGRIDWIDTH; - gridcss_column = cv_chooseskin.value / SKINGRIDWIDTH; + 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); } @@ -7288,8 +7315,9 @@ INT32 MR_HandleSetupMultiPlayerMenu(INT32 choice) case KEY_TAB: M_SetItemOn(MN_MP_PLAYERSETUP, "FOLLOWER"); CV_SetValue(&cv_chooseskin, gridcss_skinmemory); - gridcss_row = cv_chooseskin.value % SKINGRIDWIDTH; - gridcss_column = cv_chooseskin.value / SKINGRIDWIDTH; + 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); @@ -7306,7 +7334,7 @@ INT32 MR_HandleSetupMultiPlayerMenu(INT32 choice) return false; } - CV_SetValue(&cv_chooseskin, MapGridSelectToSkin(gridcss_row, gridcss_column)); + CV_SetValue(&cv_chooseskin, skinsorted[MapGridSelectToSkin(gridcss_row, gridcss_column)]); return true; } @@ -7333,6 +7361,7 @@ static void M_GetFollowerState(void) // start the multiplayer setup menu INT32 MR_SetupMultiPlayer(INT32 arg) { + INT32 sortedIndex; if (arg < 0 || arg >= MAXSPLITSCREENPLAYERS) return false; @@ -7348,8 +7377,9 @@ INT32 MR_SetupMultiPlayer(INT32 arg) CV_SetValue(&cv_dummyfollower, cv_follower[arg].value); CV_SetValue(&cv_dummycolor, cv_playercolor[arg].value); - gridcss_row = gridcss_skinmemory % SKINGRIDWIDTH; - gridcss_column = gridcss_skinmemory / SKINGRIDWIDTH; + sortedIndex = FindSortedSkinIndex(gridcss_skinmemory); + gridcss_row = sortedIndex % SKINGRIDWIDTH; + gridcss_column = sortedIndex / SKINGRIDWIDTH; gridcss_skinydrag = CLAMP(gridcss_column - SKINGRIDHEIGHT + 1, 0, ((numskins - 1) / SKINGRIDWIDTH) - SKINGRIDHEIGHT - 1); M_GetFollowerState(); // update follower state @@ -7364,7 +7394,8 @@ INT32 MR_QuitMultiPlayerMenu(INT32 choice) { (void)choice; - CV_SetValue(&cv_chooseskin, gridcss_skinmemory); + if (cv_skinselectstyle.value == 1) + CV_SetValue(&cv_chooseskin, gridcss_skinmemory); const char *followername = cv_dummyfollower.value == -1 ? "None" : followers[cv_dummyfollower.value].skinname; @@ -7377,11 +7408,12 @@ INT32 MR_QuitMultiPlayerMenu(INT32 choice) )); return true; } -#undef SKINGRIDWIDTH -#undef SKINGRIDHEIGHT #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 a5a74ef3d..58d4a8172 100644 --- a/src/m_menu.h +++ b/src/m_menu.h @@ -425,8 +425,6 @@ extern consvar_t cv_dummystaff; extern consvar_t cv_dummymultiplayer, cv_dummyip, cv_dummyname, cv_dummyfollower, cv_dummycolor; extern consvar_t cv_dummyserverpage; -extern consvar_t cv_skinselectstyle; - extern consvar_t cv_menucaps; // allow menu text to be displayed in lowercase #define MENUCAPS (!cv_menucaps.value ? V_ALLOWLOWERCASE : 0) @@ -471,6 +469,8 @@ fixed_t M_GetMapThumbnail(INT16 mapnum, patch_t **out); // Moviemode menu updating void Moviemode_option_Onchange(void); +void Skinsort_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 bd381d20c..f9ca34c40 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,121 @@ 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); +} + +void SortSkins(void) +{ + CONS_Printf("Sorting skin list (sort type %d)...\n", cv_skinselectsort.value); + + 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; + default: + _sortingFunc = CompareSkinIds; + break; + } + qsort(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) @@ -911,8 +1029,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..a7311ed28 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,21 @@ enum facepatches { NUMFACES }; +enum skinmenusortoption +{ + SKINMENUSORT_ID = 0, + SKINMENUSORT_NAME, + SKINMENUSORT_SPEED, + SKINMENUSORT_WEIGHT, + SKINMENUSORT_ACCEL, + SKINMENUSORT_HANDLING, + NUMSKINMENUSORT, +}; + /// Externs extern INT32 numskins; extern skin_t skins[MAXSKINS]; +extern INT32 skinsorted[MAXSKINS]; extern CV_PossibleValue_t Forceskin_cons_t[]; @@ -86,6 +100,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