diff --git a/src/d_netcmd.c b/src/d_netcmd.c index f6ef19d81..cb144ab45 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -1535,7 +1535,8 @@ static void SendNameAndColor(UINT8 n) K_KartResetPlayerColor(player); - if ((foundskin = R_SkinAvailable(cv_skin[n].string)) != -1 && R_SkinUsable(playernum, foundskin, false)) + foundskin = R_SkinAvailable(cv_skin[n].string); + if (foundskin != -1 && R_SkinUsable(playernum, foundskin, false)) { SetPlayerSkin(playernum, cv_skin[n].string); CV_StealthSet(&cv_skin[n], skins[foundskin].name); @@ -1553,6 +1554,8 @@ static void SendNameAndColor(UINT8 n) // Update follower for local games: foundskin = K_FollowerAvailable(cv_follower[n].string); + if (!K_FollowerUsable(foundskin)) + foundskin = -1; CV_StealthSet(&cv_follower[n], (foundskin == -1) ? "None" : followers[foundskin].name); cv_follower[n].value = foundskin; K_SetFollowerByNum(playernum, foundskin); @@ -1587,7 +1590,7 @@ static void SendNameAndColor(UINT8 n) } cv_follower[n].value = K_FollowerAvailable(cv_follower[n].string); - if (cv_follower[n].value < 0) + if (cv_follower[n].value < 0 || !K_FollowerUsable(cv_follower[n].value)) { CV_StealthSet(&cv_follower[n], "None"); cv_follower[n].value = -1; diff --git a/src/deh_soc.c b/src/deh_soc.c index 87cc55328..9469e3858 100644 --- a/src/deh_soc.c +++ b/src/deh_soc.c @@ -2265,8 +2265,8 @@ void readunlockable(MYFILE *f, INT32 num) unlockables[num].type = SECRET_HEADER; else if (fastcmp(word2, "SKIN")) unlockables[num].type = SECRET_SKIN; - else if (fastcmp(word2, "WARP")) - unlockables[num].type = SECRET_WARP; + else if (fastcmp(word2, "FOLLOWER")) + unlockables[num].type = SECRET_FOLLOWER; else if (fastcmp(word2, "TIMEATTACK")) unlockables[num].type = SECRET_TIMEATTACK; else if (fastcmp(word2, "BREAKTHECAPSULES")) diff --git a/src/g_demo.c b/src/g_demo.c index c6ee247e2..9d4f1123a 100644 --- a/src/g_demo.c +++ b/src/g_demo.c @@ -212,47 +212,6 @@ void G_LoadMetal(UINT8 **buffer) metal_p = metalbuffer + READUINT32(*buffer); } -// Finds a skin with the closest stats if the expected skin doesn't exist. -static INT32 GetSkinNumClosestToStats(UINT8 kartspeed, UINT8 kartweight, UINT32 flags) -{ - INT32 i, closest_skin = 0; - UINT8 closest_stats, stat_diff; - boolean doflagcheck = true; - UINT32 flagcheck = flags; - -flaglessretry: - closest_stats = UINT8_MAX; - - for (i = 0; i < numskins; i++) - { - stat_diff = abs(skins[i].kartspeed - kartspeed) + abs(skins[i].kartweight - kartweight); - if (doflagcheck && (skins[i].flags & flagcheck) != flagcheck) - { - continue; - } - if (stat_diff < closest_stats) - { - closest_stats = stat_diff; - closest_skin = i; - } - } - - if (stat_diff && (doflagcheck || closest_stats == UINT8_MAX)) - { - // Just grab *any* SF_IRONMAN if we don't get it on the first pass. - if ((flagcheck & SF_IRONMAN) && (flagcheck != SF_IRONMAN)) - { - flagcheck = SF_IRONMAN; - } - - doflagcheck = false; - - goto flaglessretry; - } - - return closest_skin; -} - void G_ReadDemoExtraData(void) { INT32 p, extradata, i; @@ -2321,7 +2280,7 @@ static democharlist_t *G_LoadDemoSkins(UINT8 **pp, UINT8 *worknumskins, boolean } else { - result = GetSkinNumClosestToStats(skinlist[i].kartspeed, skinlist[i].kartweight, skinlist[i].flags); + result = GetSkinNumClosestToStats(skinlist[i].kartspeed, skinlist[i].kartweight, skinlist[i].flags, true); } } diff --git a/src/k_follower.c b/src/k_follower.c index 636620ec8..b340ba266 100644 --- a/src/k_follower.c +++ b/src/k_follower.c @@ -11,6 +11,7 @@ #include "r_skins.h" #include "p_local.h" #include "p_mobj.h" +#include "m_cond.h" INT32 numfollowers = 0; follower_t followers[MAXSKINS]; @@ -38,6 +39,49 @@ INT32 K_FollowerAvailable(const char *name) return -1; } +/*-------------------------------------------------- + INT32 K_FollowerUsable(INT32 followernum) + + See header file for description. +--------------------------------------------------*/ +boolean K_FollowerUsable(INT32 skinnum) +{ + // Unlike R_SkinUsable, not netsynced. + // Solely used to prevent an invalid value being sent over the wire. + UINT8 i; + INT32 fid; + + if (skinnum == -1 || demo.playback) + { + // Simplifies things elsewhere, since there's already plenty of checks for less-than-0... + return true; + } + + // Determine if this follower is supposed to be unlockable or not + for (i = 0; i < MAXUNLOCKABLES; i++) + { + if (unlockables[i].type != SECRET_FOLLOWER) + continue; + + fid = M_UnlockableFollowerNum(&unlockables[i]); + + if (fid != skinnum) + continue; + + // i is now the unlockable index, we can use this later + break; + } + + if (i == MAXUNLOCKABLES) + { + // Didn't trip anything, so we can use this follower. + return true; + } + + // Use the unlockables table directly + return (boolean)(gamedata->unlocked[i]); +} + /*-------------------------------------------------- boolean K_SetFollowerByName(INT32 playernum, const char *skinname) diff --git a/src/k_follower.h b/src/k_follower.h index 3c1e2a7d1..323f80b22 100644 --- a/src/k_follower.h +++ b/src/k_follower.h @@ -115,6 +115,22 @@ extern followercategory_t followercategories[MAXFOLLOWERCATEGORIES]; INT32 K_FollowerAvailable(const char *name); +/*-------------------------------------------------- + boolean K_FollowerUsable(INT32 followernum); + + Check if a follower is usable or not. + + Input Arguments:- + skinnum - The follower's skin ID + + Return:- + true if it was a valid follower, + otherwise false. +--------------------------------------------------*/ + +boolean K_FollowerUsable(INT32 skinnum); + + /*-------------------------------------------------- boolean K_SetFollowerByName(INT32 playernum, const char *skinname) diff --git a/src/k_menu.h b/src/k_menu.h index 43a4dd90d..49576eaad 100644 --- a/src/k_menu.h +++ b/src/k_menu.h @@ -596,6 +596,9 @@ extern struct setup_chargrid_s { UINT8 numskins; } setup_chargrid[9][9]; +extern UINT8 setup_followercategories[MAXFOLLOWERCATEGORIES][2]; +extern UINT8 setup_numfollowercategories; + typedef enum { CSSTEP_NONE = 0, diff --git a/src/k_menudraw.c b/src/k_menudraw.c index a3cae5651..ea0bed6e4 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -981,10 +981,10 @@ static void M_DrawCharSelectCircle(setup_player_t *p, INT16 x, INT16 y) numoptions = nummenucolors; break; case CSSTEP_FOLLOWERCATEGORY: - numoptions = numfollowercategories+1; + numoptions = setup_numfollowercategories+1; break; case CSSTEP_FOLLOWER: - numoptions = followercategories[p->followercategory].numincategory; + numoptions = setup_followercategories[p->followercategory][0]; break; case CSSTEP_FOLLOWERCOLORS: numoptions = nummenucolors+2; @@ -1084,7 +1084,7 @@ static void M_DrawCharSelectCircle(setup_player_t *p, INT16 x, INT16 y) } else { - fc = &followercategories[n - 1]; + fc = &followercategories[setup_followercategories[n - 1][1]]; patch = W_CachePatchName(fc->icon, PU_CACHE); } @@ -1111,7 +1111,7 @@ static void M_DrawCharSelectCircle(setup_player_t *p, INT16 x, INT16 y) n = numfollowers-1; if (n == startfollowern) break; - if (followers[n].category == p->followercategory) + if (followers[n].category == setup_followercategories[p->followercategory][1]) r--; } l = r = n; @@ -1126,7 +1126,7 @@ static void M_DrawCharSelectCircle(setup_player_t *p, INT16 x, INT16 y) if (l == startfollowern) break; } - while (followers[l].category != p->followercategory); + while (followers[l].category != setup_followercategories[p->followercategory][1]); n = l; } else @@ -1139,7 +1139,7 @@ static void M_DrawCharSelectCircle(setup_player_t *p, INT16 x, INT16 y) if (r == startfollowern) break; } - while (followers[r].category != p->followercategory); + while (followers[r].category != setup_followercategories[p->followercategory][1]); n = r; } @@ -1266,7 +1266,7 @@ static boolean M_DrawCharacterSprite(INT16 x, INT16 y, INT16 skin, boolean charf // Returns false is the follower shouldn't be rendered. // 'num' can be used to directly specify the follower number, but doing this will not animate it. // if a setup_player_t is specified instead, its data will be used to animate the follower sprite. -static boolean M_DrawFollowerSprite(INT16 x, INT16 y, INT32 num, boolean charflip, INT32 addflags, UINT16 color, setup_player_t *p) +static boolean M_DrawFollowerSprite(INT16 x, INT16 y, INT32 num, boolean charflip, INT32 addflags, UINT8 *colormap, setup_player_t *p) { spritedef_t *sprdef; @@ -1276,7 +1276,6 @@ static boolean M_DrawFollowerSprite(INT16 x, INT16 y, INT32 num, boolean charfli state_t *usestate; UINT32 useframe; follower_t fl; - UINT8 *colormap = NULL; UINT8 rotation = (charflip ? 1 : 7); if (p != NULL) @@ -1320,13 +1319,13 @@ static boolean M_DrawFollowerSprite(INT16 x, INT16 y, INT32 num, boolean charfli if (p != NULL) { - sine = FixedMul(fl.bobamp, FINESINE(((FixedMul(4 * M_TAU_FIXED, fl.bobspeed) * p->follower_timer)>>ANGLETOFINESHIFT) & FINEMASK)); - color = K_GetEffectiveFollowerColor( + UINT16 color = K_GetEffectiveFollowerColor( (p->mdepth < CSSTEP_FOLLOWERCOLORS) ? fl.defaultcolor : p->followercolor, p->color); + sine = FixedMul(fl.bobamp, FINESINE(((FixedMul(4 * M_TAU_FIXED, fl.bobspeed) * p->follower_timer)>>ANGLETOFINESHIFT) & FINEMASK)); + colormap = R_GetTranslationColormap(TC_DEFAULT, color, GTC_MENUCACHE); } - colormap = R_GetTranslationColormap(TC_DEFAULT, color, GTC_MENUCACHE); V_DrawFixedPatch((x*FRACUNIT), ((y-12)*FRACUNIT) + sine, fl.scale, addflags, patch, colormap); return true; @@ -1540,6 +1539,9 @@ static void M_DrawCharSelectCursor(UINT8 num) INT16 x, y; INT16 quadx, quady; + if (p->mdepth < CSSTEP_ASKCHANGES) + return; + quadx = 4 * (p->gridx / 3); quady = 4 * (p->gridy / 3); @@ -1583,6 +1585,7 @@ static void M_DrawProfileCard(INT32 x, INT32 y, boolean greyedout, profile_t *p) patch_t *card = W_CachePatchName("PR_CARD", PU_CACHE); patch_t *cardbot = W_CachePatchName("PR_CARDB", PU_CACHE); patch_t *pwrlv = W_CachePatchName("PR_PWR", PU_CACHE); + UINT16 truecol = SKINCOLOR_BLACK; UINT8 *colormap = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_BLACK, GTC_CACHE); INT32 skinnum = -1; INT32 powerlevel = -1; @@ -1591,7 +1594,8 @@ static void M_DrawProfileCard(INT32 x, INT32 y, boolean greyedout, profile_t *p) if (p != NULL && p->version) { - colormap = R_GetTranslationColormap(TC_DEFAULT, PR_GetProfileColor(p), GTC_CACHE); + truecol = PR_GetProfileColor(p); + colormap = R_GetTranslationColormap(TC_DEFAULT, truecol, GTC_CACHE); strcpy(pname, p->profilename); skinnum = R_SkinAvailable(p->skinname); powerlevel = p->powerlevels[0]; // Only display race power level. @@ -1600,7 +1604,10 @@ static void M_DrawProfileCard(INT32 x, INT32 y, boolean greyedout, profile_t *p) // check setup_player for colormap for the card. // we'll need to check again for drawing afterwards unfortunately. if (sp->mdepth >= CSSTEP_CHARS) + { + truecol = PR_GetProfileColor(p); colormap = R_GetTranslationColormap(skinnum, sp->color, GTC_MENUCACHE); + } // Card V_DrawFixedPatch(x*FRACUNIT, y*FRACUNIT, FRACUNIT, greyedout ? V_TRANSLUCENT : 0, card, colormap); @@ -1634,25 +1641,34 @@ static void M_DrawProfileCard(INT32 x, INT32 y, boolean greyedout, profile_t *p) { UINT16 col = K_GetEffectiveFollowerColor(sp->followercolor, sp->color);; patch_t *ico = W_CachePatchName(followers[sp->followern].icon, PU_CACHE); - UINT8 *fcolormap; - - fcolormap = R_GetTranslationColormap(TC_DEFAULT, col, GTC_MENUCACHE); + UINT8 *fcolormap = R_GetTranslationColormap(TC_DEFAULT, col, GTC_MENUCACHE); V_DrawMappedPatch(x+14+18, y+66, 0, ico, fcolormap); } } } else if (skinnum > -1) // otherwise, read from profile. { - UINT16 col = K_GetEffectiveFollowerColor(p->followercolor, p->color);; + UINT8 *ccolormap, *fcolormap; + UINT16 col = K_GetEffectiveFollowerColor(p->followercolor, p->color); UINT8 fln = K_FollowerAvailable(p->follower); - if (M_DrawCharacterSprite(x-22, y+119, skinnum, false, false, 0, colormap)) - V_DrawMappedPatch(x+14, y+66, 0, faceprefix[skinnum][FACE_RANK], colormap); + if (R_SkinUsable(g_localplayers[0], skinnum, false)) + ccolormap = colormap; + else + ccolormap = R_GetTranslationColormap(TC_BLINK, truecol, GTC_MENUCACHE); - if (M_DrawFollowerSprite(x-22 - 16, y+119, fln, false, 0, col, NULL)) + fcolormap = R_GetTranslationColormap( + (K_FollowerUsable(fln) ? TC_DEFAULT : TC_BLINK), + col, GTC_MENUCACHE); + + if (M_DrawCharacterSprite(x-22, y+119, skinnum, false, false, 0, ccolormap)) + { + V_DrawMappedPatch(x+14, y+66, 0, faceprefix[skinnum][FACE_RANK], ccolormap); + } + + if (M_DrawFollowerSprite(x-22 - 16, y+119, fln, false, 0, fcolormap, NULL)) { patch_t *ico = W_CachePatchName(followers[fln].icon, PU_CACHE); - UINT8 *fcolormap = R_GetTranslationColormap(TC_DEFAULT, col, GTC_MENUCACHE); V_DrawMappedPatch(x+14+18, y+66, 0, ico, fcolormap); } } @@ -1718,8 +1734,11 @@ void M_DrawCharacterSelect(void) { for (k = 0; k < setup_numplayers; k++) { - if (setup_player[k].gridx == i && setup_player[k].gridy == j) - break; // k == setup_numplayers means no one has it selected + if (setup_player[k].mdepth < CSSTEP_ASKCHANGES) + continue; + if (setup_player[k].gridx != i || setup_player[k].gridy != j) + continue; + break; // k == setup_numplayers means no one has it selected } skin = setup_chargrid[i][j].skinlist[setup_page]; @@ -4532,7 +4551,6 @@ void M_DrawChallenges(void) } else switch (ref->type) { - // add all SECRET_ENCORE, etc up here before SECRET_SKIN case SECRET_SKIN: { INT32 skin = M_UnlockableSkinNum(ref); @@ -4543,6 +4561,17 @@ void M_DrawChallenges(void) } break; } + case SECRET_FOLLOWER: + { + INT32 skin = M_UnlockableFollowerNum(ref); + if (skin != -1) + { + UINT16 col = K_GetEffectiveFollowerColor(followers[skin].defaultcolor, cv_playercolor[0].value); + colormap = R_GetTranslationColormap(skin, col, GTC_MENUCACHE); + pat = W_CachePatchName(followers[skin].icon, PU_CACHE); + } + break; + } default: { pat = W_CachePatchName(va("UN_RR00%c", ref->majorunlock ? 'B' : 'A'), PU_CACHE); diff --git a/src/k_menufunc.c b/src/k_menufunc.c index b1c34c19f..86cc29ffe 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -2053,7 +2053,10 @@ void M_QuitSRB2(INT32 choice) struct setup_chargrid_s setup_chargrid[9][9]; setup_player_t setup_player[MAXSPLITSCREENPLAYERS]; -struct setup_explosions_s setup_explosions[48]; +struct setup_explosions_s setup_explosions[CSEXPLOSIONS]; + +UINT8 setup_followercategories[MAXFOLLOWERCATEGORIES][2]; +UINT8 setup_numfollowercategories; UINT8 setup_numplayers = 0; // This variable is very important, it was extended to determine how many players exist in ALL menus. tic_t setup_animcounter = 0; @@ -2065,64 +2068,61 @@ UINT8 setup_maxpage = 0; // For charsel page to identify alts easier... static void M_SetupProfileGridPos(setup_player_t *p) { profile_t *pr = PR_GetProfile(p->profilen); - INT32 i; + INT32 i = R_SkinAvailable(pr->skinname); + INT32 alt = 0; // Hey it's my character's name! // While we're here, read follower values. p->followern = K_FollowerAvailable(pr->follower); - if (p->followern < 0 || p->followern >= numfollowers || followers[p->followern].category >= numfollowercategories) - p->followercategory = -1; + if (p->followern < 0 || p->followern >= numfollowers || followers[p->followern].category >= numfollowercategories || !K_FollowerUsable(p->followern)) + p->followercategory = p->followern = -1; else p->followercategory = followers[p->followern].category; p->followercolor = pr->followercolor; - // Now position the grid for skin - for (i = 0; i < numskins; i++) + if (!R_SkinUsable(g_localplayers[0], i, false)) { - if (!(strcmp(pr->skinname, skins[i].name))) - { - INT32 alt = 0; // Hey it's my character's name! - p->gridx = skins[i].kartspeed-1; - p->gridy = skins[i].kartweight-1; - - // Now this put our cursor on the good alt - while (setup_chargrid[p->gridx][p->gridy].skinlist[alt] != i) - alt++; - - p->clonenum = alt; - p->color = PR_GetProfileColor(pr); - return; // we're done here - } + i = GetSkinNumClosestToStats(skins[i].kartspeed, skins[i].kartweight, skins[i].flags, false); } + + // Now position the grid for skin + p->gridx = skins[i].kartspeed-1; + p->gridy = skins[i].kartweight-1; + + // Now this put our cursor on the good alt + while (alt < setup_chargrid[p->gridx][p->gridy].numskins && setup_chargrid[p->gridx][p->gridy].skinlist[alt] != i) + alt++; + + p->clonenum = alt; + p->color = PR_GetProfileColor(pr); } static void M_SetupMidGameGridPos(setup_player_t *p, UINT8 num) { - INT32 i; + INT32 i = R_SkinAvailable(cv_skin[num].zstring); + INT32 alt = 0; // Hey it's my character's name! // While we're here, read follower values. p->followern = cv_follower[num].value; p->followercolor = cv_followercolor[num].value; + if (p->followern < 0 || p->followern >= numfollowers || followers[p->followern].category >= numfollowercategories || !K_FollowerUsable(p->followern)) + p->followercategory = p->followern = -1; + else + p->followercategory = followers[p->followern].category; + // Now position the grid for skin - for (i = 0; i < numskins; i++) - { - if (!(strcmp(cv_skin[num].zstring, skins[i].name))) - { - INT32 alt = 0; // Hey it's my character's name! - p->gridx = skins[i].kartspeed-1; - p->gridy = skins[i].kartweight-1; + p->gridx = skins[i].kartspeed-1; + p->gridy = skins[i].kartweight-1; - // Now this put our cursor on the good alt - while (setup_chargrid[p->gridx][p->gridy].skinlist[alt] != i) - alt++; + // Now this put our cursor on the good alt + while (alt < setup_chargrid[p->gridx][p->gridy].numskins && setup_chargrid[p->gridx][p->gridy].skinlist[alt] != i) + alt++; - p->clonenum = alt; - p->color = cv_playercolor[num].value; - return; // we're done here - } - } + p->clonenum = alt; + p->color = cv_playercolor[num].value; + return; // we're done here } @@ -2219,6 +2219,32 @@ void M_CharacterSelectInit(void) } } + setup_numfollowercategories = 0; + for (i = 0; i < numfollowercategories; i++) + { + if (followercategories[i].numincategory == 0) + continue; + + setup_followercategories[setup_numfollowercategories][0] = 0; + + for (j = 0; j < numfollowers; j++) + { + if (followers[j].category != i) + continue; + + if (!K_FollowerUsable(j)) + continue; + + setup_followercategories[setup_numfollowercategories][0]++; + setup_followercategories[setup_numfollowercategories][1] = i; + } + + if (!setup_followercategories[setup_numfollowercategories][0]) + continue; + + setup_numfollowercategories++; + } + setup_page = 0; } @@ -2843,7 +2869,7 @@ static void M_HandleFollowerCategoryRotate(setup_player_t *p, UINT8 num) if (menucmd[num].dpad_lr > 0) { p->followercategory++; - if (p->followercategory >= numfollowercategories) + if (p->followercategory >= setup_numfollowercategories) p->followercategory = -1; p->rotate = CSROTATETICS; @@ -2854,7 +2880,7 @@ static void M_HandleFollowerCategoryRotate(setup_player_t *p, UINT8 num) { p->followercategory--; if (p->followercategory < -1) - p->followercategory = numfollowercategories-1; + p->followercategory = setup_numfollowercategories-1; p->rotate = -CSROTATETICS; p->delay = CSROTATETICS; @@ -2876,7 +2902,9 @@ static void M_HandleFollowerCategoryRotate(setup_player_t *p, UINT8 num) if (p->followern < 0 || followers[p->followern].category != p->followercategory) { p->followern = 0; - while (p->followern < numfollowers && followers[p->followern].category != p->followercategory) + while (p->followern < numfollowers + && (followers[p->followern].category != setup_followercategories[p->followercategory][1] + || !K_FollowerUsable(p->followern))) p->followern++; } @@ -2931,7 +2959,7 @@ static void M_HandleFollowerRotate(setup_player_t *p, UINT8 num) if (p->followern == startfollowern) break; } - while (followers[p->followern].category != p->followercategory); + while (followers[p->followern].category != setup_followercategories[p->followercategory][1] || !K_FollowerUsable(p->followern)); M_GetFollowerState(p); @@ -2949,7 +2977,7 @@ static void M_HandleFollowerRotate(setup_player_t *p, UINT8 num) if (p->followern == startfollowern) break; } - while (followers[p->followern].category != p->followercategory); + while (followers[p->followern].category != setup_followercategories[p->followercategory][1] || !K_FollowerUsable(p->followern)); M_GetFollowerState(p); diff --git a/src/m_cond.c b/src/m_cond.c index 46f81cfc8..74e8c09fd 100644 --- a/src/m_cond.c +++ b/src/m_cond.c @@ -20,6 +20,7 @@ #include "g_game.h" // record info #include "r_skins.h" // numskins +#include "k_follower.h" #include "r_draw.h" // R_GetColorByName #include "k_pwrlv.h" @@ -860,7 +861,7 @@ INT32 M_UnlockableSkinNum(unlockable_t *unlock) return -1; } - if (unlock->stringVar && strcmp(unlock->stringVar, "")) + if (unlock->stringVar && unlock->stringVar[0]) { // Get the skin from the string. INT32 skinnum = R_SkinAvailable(unlock->stringVar); @@ -880,6 +881,35 @@ INT32 M_UnlockableSkinNum(unlockable_t *unlock) return -1; } +// Gets the skin number for a SECRET_FOLLOWER unlockable. +INT32 M_UnlockableFollowerNum(unlockable_t *unlock) +{ + if (unlock->type != SECRET_FOLLOWER) + { + // This isn't a follower unlockable... + return -1; + } + + if (unlock->stringVar && unlock->stringVar[0]) + { + // Get the skin from the string. + INT32 skinnum = K_FollowerAvailable(unlock->stringVar); + if (skinnum != -1) + { + return skinnum; + } + } + + if (unlock->variable >= 0 && unlock->variable < numfollowers) + { + // Use the number directly. + return unlock->variable; + } + + // Invalid follower unlockable. + return -1; +} + // ---------------- // Misc Emblem shit // ---------------- diff --git a/src/m_cond.h b/src/m_cond.h index f839155fa..d7445cd68 100644 --- a/src/m_cond.h +++ b/src/m_cond.h @@ -96,7 +96,7 @@ typedef struct #define SECRET_HEADER 1 // Does nothing on its own, just serves as a header for the menu #define SECRET_SKIN 2 // Allow this character to be selected -#define SECRET_WARP 3 // Selectable warp (todo Followers) +#define SECRET_FOLLOWER 3 // Allow this follower to be selected #define SECRET_EXTRAEMBLEM 4 // Extra Emblems (formerly extraemblem_t) @@ -201,6 +201,7 @@ UINT8 M_GotEnoughEmblems(INT32 number); UINT8 M_GotLowEnoughTime(INT32 tictime); INT32 M_UnlockableSkinNum(unlockable_t *unlock); +INT32 M_UnlockableFollowerNum(unlockable_t *unlock); INT32 M_EmblemSkinNum(emblem_t *emblem); #define M_Achieved(a) ((a) >= MAXCONDITIONSETS || gamedata->achieved[a]) diff --git a/src/r_skins.c b/src/r_skins.c index 03753d1b5..3fea4b2cc 100644 --- a/src/r_skins.c +++ b/src/r_skins.c @@ -505,6 +505,51 @@ void ClearFakePlayerSkin(player_t* player) player->fakeskin = MAXSKINS; } +// Finds a skin with the closest stats if the expected skin doesn't exist. +INT32 GetSkinNumClosestToStats(UINT8 kartspeed, UINT8 kartweight, UINT32 flags, boolean unlock) +{ + INT32 i, closest_skin = 0; + UINT8 closest_stats, stat_diff; + boolean doflagcheck = true; + UINT32 flagcheck = flags; + +flaglessretry: + closest_stats = stat_diff = UINT8_MAX; + + for (i = 0; i < numskins; i++) + { + if (!unlock && !R_SkinUsable(-1, i, false)) + { + continue; + } + stat_diff = abs(skins[i].kartspeed - kartspeed) + abs(skins[i].kartweight - kartweight); + if (doflagcheck && (skins[i].flags & flagcheck) != flagcheck) + { + continue; + } + if (stat_diff < closest_stats) + { + closest_stats = stat_diff; + closest_skin = i; + } + } + + if (stat_diff && (doflagcheck || closest_stats == UINT8_MAX)) + { + // Just grab *any* SF_IRONMAN if we don't get it on the first pass. + if ((flagcheck & SF_IRONMAN) && (flagcheck != SF_IRONMAN)) + { + flagcheck = SF_IRONMAN; + } + + doflagcheck = false; + + goto flaglessretry; + } + + return closest_skin; +} + // // Add skins from a pwad, each skin preceded by 'S_SKIN' marker // diff --git a/src/r_skins.h b/src/r_skins.h index 7b32d3e0c..fcc710f2b 100644 --- a/src/r_skins.h +++ b/src/r_skins.h @@ -85,6 +85,7 @@ void SetFakePlayerSkin(player_t* player, INT32 skinnum); void SetRandomFakePlayerSkin(player_t* player, boolean fast); void ClearFakePlayerSkin(player_t* player); boolean R_SkinUsable(INT32 playernum, INT32 skinnum, boolean demoskins); +INT32 GetSkinNumClosestToStats(UINT8 kartspeed, UINT8 kartweight, UINT32 flags, boolean unlock); UINT8 *R_GetSkinAvailabilities(boolean demolock); INT32 R_SkinAvailable(const char *name);