SECRET_FOLLOWER

Completely clientside, unlike SECRET_SKIN. Followers have no gameplay function, and it saves us having to write even more to our demos/netsaves. Replaces SECRET_WARP.
Also:
- Now don't apply skins or followers from profiles if the skin is locked - instead, get the closest skin in stats. (Same function as from demos!)
- If you're looking at a profile and the skin or follower are locked, make them solid colour (TC_BLINK).
- Don't show the ring cursor before you've selected a profile.
This commit is contained in:
toaster 2022-12-04 01:09:08 +00:00
parent a33d6d9235
commit fefaee1982
12 changed files with 271 additions and 112 deletions

View file

@ -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;

View file

@ -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"))

View file

@ -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);
}
}

View file

@ -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)

View file

@ -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)

View file

@ -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,

View file

@ -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);

View file

@ -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);

View file

@ -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
// ----------------

View file

@ -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])

View file

@ -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
//

View file

@ -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);