Clean up follower setting to handle strings and values just like player->skin

- Fixes defaulting to follower id 0 (previously chao, currently hot robo) when joining a server without going through the menu flow
- No need to store TWO names for a follower, so save a little memory
- Should make it easier to add unlockable followers later
This commit is contained in:
toaster 2022-11-07 18:11:06 +00:00
parent 35a3a9db4a
commit 2e75012645
6 changed files with 56 additions and 206 deletions

View file

@ -1528,31 +1528,28 @@ static void SendNameAndColor(UINT8 n)
K_KartResetPlayerColor(player); K_KartResetPlayerColor(player);
// Update follower for local games: if ((foundskin = R_SkinAvailable(cv_skin[n].string)) != -1 && R_SkinUsable(playernum, foundskin))
if (cv_follower[n].value >= -1 && cv_follower[n].value != player->followerskin)
K_SetFollowerByNum(playernum, cv_follower[n].value);
player->followercolor = cv_followercolor[n].value;
if (metalrecording && n == 0)
{ // Starring Metal Sonic as themselves, obviously.
SetPlayerSkinByNum(playernum, 5);
CV_StealthSet(&cv_skin[n], skins[5].name);
}
else if ((foundskin = R_SkinAvailable(cv_skin[n].string)) != -1 && R_SkinUsable(playernum, foundskin))
{ {
cv_skin[n].value = foundskin;
SetPlayerSkin(playernum, cv_skin[n].string); SetPlayerSkin(playernum, cv_skin[n].string);
CV_StealthSet(&cv_skin[n], skins[cv_skin[n].value].name); CV_StealthSet(&cv_skin[n], skins[foundskin].name);
cv_skin[n].value = foundskin;
} }
else else
{ {
cv_skin[n].value = players[playernum].skin;
CV_StealthSet(&cv_skin[n], skins[player->skin].name); CV_StealthSet(&cv_skin[n], skins[player->skin].name);
cv_skin[n].value = player->skin;
// will always be same as current // will always be same as current
SetPlayerSkin(playernum, cv_skin[n].string); SetPlayerSkin(playernum, cv_skin[n].string);
} }
player->followercolor = cv_followercolor[n].value;
// Update follower for local games:
foundskin = K_FollowerAvailable(cv_follower[n].string);
CV_StealthSet(&cv_follower[n], (foundskin == -1) ? "None" : followers[foundskin].name);
cv_follower[n].value = foundskin;
K_SetFollowerByNum(playernum, foundskin);
return; return;
} }
@ -1582,6 +1579,13 @@ static void SendNameAndColor(UINT8 n)
cv_skin[n].value = 0; cv_skin[n].value = 0;
} }
cv_follower[n].value = K_FollowerAvailable(cv_follower[n].string);
if (cv_follower[n].value < 0)
{
CV_StealthSet(&cv_follower[n], "None");
cv_follower[n].value = -1;
}
// Finally write out the complete packet and send it off. // Finally write out the complete packet and send it off.
WRITESTRINGN(p, cv_playername[n].zstring, MAXPLAYERNAME); WRITESTRINGN(p, cv_playername[n].zstring, MAXPLAYERNAME);
WRITEUINT32(p, (UINT32)player->availabilities); WRITEUINT32(p, (UINT32)player->availabilities);
@ -6096,207 +6100,56 @@ static void Name4_OnChange(void)
} }
// sends the follower change for players // sends the follower change for players
static void Follower_OnChange(void) static void FollowerAny_OnChange(UINT8 pnum)
{ {
char str[SKINNAMESIZE+1], cpy[SKINNAMESIZE+1];
INT32 num;
// there is a slight chance that we will actually use a string instead so...
// let's investigate the string...
strcpy(str, cv_follower[0].string);
strcpy(cpy, cv_follower[0].string);
strlwr(str);
if (stricmp(cpy,"0") !=0 && !atoi(cpy)) // yep, that's a string alright...
{
if (stricmp(cpy, "None") == 0)
{
CV_StealthSet(&cv_follower[0], "-1");
if (!Playing())
return; // don't send anything there.
SendNameAndColor(0);
return;
}
num = K_FollowerAvailable(str);
if (num == -1) // that's an error.
CONS_Alert(CONS_WARNING, M_GetText("Follower '%s' not found\n"), str);
CV_StealthSet(&cv_follower[0], str);
cv_follower[0].value = num;
}
if (!Playing()) if (!Playing())
return; // don't send anything there. return; // don't send anything there.
SendNameAndColor(0); SendNameAndColor(pnum);
}
// sends the follower change for players
static void Follower_OnChange(void)
{
FollowerAny_OnChange(0);
} }
// About the same as Color_OnChange but for followers. // About the same as Color_OnChange but for followers.
static void Followercolor_OnChange(void) static void Followercolor_OnChange(void)
{ {
if (!Playing()) FollowerAny_OnChange(0);
return; // do whatever you want if you aren't in the game or don't have a follower.
if (!P_PlayerMoving(consoleplayer))
{
// Color change menu scrolling fix is no longer necessary
SendNameAndColor(0);
}
} }
// repeat for the 3 other players // repeat for the 3 other players
static void Follower2_OnChange(void) static void Follower2_OnChange(void)
{ {
char str[SKINNAMESIZE+1], cpy[SKINNAMESIZE+1]; FollowerAny_OnChange(1);
INT32 num;
// there is a slight chance that we will actually use a string instead so...
// let's investigate the string...
strcpy(str, cv_follower[1].string);
strcpy(cpy, cv_follower[1].string);
strlwr(str);
if (stricmp(cpy,"0") !=0 && !atoi(cpy)) // yep, that's a string alright...
{
if (stricmp(cpy, "None") == 0)
{
CV_StealthSet(&cv_follower[1], "-1");
if (!Playing())
return; // don't send anything there.
SendNameAndColor(1);
return;
}
num = K_FollowerAvailable(str);
if (num == -1) // that's an error.
CONS_Alert(CONS_WARNING, M_GetText("Follower '%s' not found\n"), str);
CV_StealthSet(&cv_follower[1], str);
cv_follower[1].value = num;
}
if (!Playing())
return; // don't send anything there.
SendNameAndColor(1);
} }
static void Followercolor2_OnChange(void) static void Followercolor2_OnChange(void)
{ {
if (!Playing()) FollowerAny_OnChange(1);
return; // do whatever you want if you aren't in the game or don't have a follower.
if (!P_PlayerMoving(g_localplayers[1]))
{
// Color change menu scrolling fix is no longer necessary
SendNameAndColor(1);
}
} }
static void Follower3_OnChange(void) static void Follower3_OnChange(void)
{ {
char str[SKINNAMESIZE+1], cpy[SKINNAMESIZE+1]; FollowerAny_OnChange(2);
INT32 num;
// there is a slight chance that we will actually use a string instead so...
// let's investigate the string...
strcpy(str, cv_follower[2].string);
strcpy(cpy, cv_follower[2].string);
strlwr(str);
if (stricmp(cpy,"0") !=0 && !atoi(cpy)) // yep, that's a string alright...
{
if (stricmp(cpy, "None") == 0)
{
CV_StealthSet(&cv_follower[2], "-1");
if (!Playing())
return; // don't send anything there.
SendNameAndColor(2);
return;
}
num = K_FollowerAvailable(str);
if (num == -1) // that's an error.
CONS_Alert(CONS_WARNING, M_GetText("Follower '%s' not found\n"), str);
CV_StealthSet(&cv_follower[2], str);
cv_follower[2].value = num;
}
if (!Playing())
return; // don't send anything there.
SendNameAndColor(2);
} }
static void Followercolor3_OnChange(void) static void Followercolor3_OnChange(void)
{ {
if (!Playing()) FollowerAny_OnChange(2);
return; // do whatever you want if you aren't in the game or don't have a follower.
if (!P_PlayerMoving(g_localplayers[2]))
{
// Color change menu scrolling fix is no longer necessary
SendNameAndColor(2);
}
} }
static void Follower4_OnChange(void) static void Follower4_OnChange(void)
{ {
char str[SKINNAMESIZE+1], cpy[SKINNAMESIZE+1]; FollowerAny_OnChange(3);
INT32 num;
// there is a slight chance that we will actually use a string instead so...
// let's investigate the string...
strcpy(str, cv_follower[3].string);
strcpy(cpy, cv_follower[3].string);
strlwr(str);
if (stricmp(cpy,"0") !=0 && !atoi(cpy)) // yep, that's a string alright...
{
if (stricmp(cpy, "None") == 0)
{
CV_StealthSet(&cv_follower[3], "-1");
if (!Playing())
return; // don't send anything there.
SendNameAndColor(3);
return;
}
num = K_FollowerAvailable(str);
if (num == -1) // that's an error.
CONS_Alert(CONS_WARNING, M_GetText("Follower '%s' not found\n"), str);
CV_StealthSet(&cv_follower[3], str);
cv_follower[3].value = num;
}
if (!Playing())
return; // don't send anything there.
SendNameAndColor(3);
} }
static void Followercolor4_OnChange(void) static void Followercolor4_OnChange(void)
{ {
if (!Playing()) FollowerAny_OnChange(3);
return; // do whatever you want if you aren't in the game or don't have a follower.
if (!P_PlayerMoving(g_localplayers[3]))
{
// Color change menu scrolling fix is no longer necessary
SendNameAndColor(3);
}
} }
/** Sends a skin change for the console player, unless that player is moving. Also forces them to spectate if the change is done during gameplay /** Sends a skin change for the console player, unless that player is moving. Also forces them to spectate if the change is done during gameplay

View file

@ -3130,7 +3130,7 @@ void readcupheader(MYFILE *f, cupheader_t *cup)
void readfollower(MYFILE *f) void readfollower(MYFILE *f)
{ {
char *s; char *s;
char *word, *word2, dname[SKINNAMESIZE+1]; char *word, *word2;
char *tmp; char *tmp;
char testname[SKINNAMESIZE+1]; char testname[SKINNAMESIZE+1];
@ -3348,10 +3348,6 @@ void readfollower(MYFILE *f)
// set skin name (this is just the follower's name in lowercases): // set skin name (this is just the follower's name in lowercases):
// but before we do, let's... actually check if another follower isn't doing the same shit... // but before we do, let's... actually check if another follower isn't doing the same shit...
strcpy(testname, followers[numfollowers].name);
// lower testname for skin checks...
strlwr(testname);
res = K_FollowerAvailable(testname); res = K_FollowerAvailable(testname);
if (res > -1) // yikes, someone else has stolen our name already if (res > -1) // yikes, someone else has stolen our name already
{ {
@ -3363,8 +3359,7 @@ void readfollower(MYFILE *f)
// in that case, we'll be very lazy and copy numfollowers to the end of our skin name. // in that case, we'll be very lazy and copy numfollowers to the end of our skin name.
} }
strcpy(followers[numfollowers].skinname, testname); strcpy(testname, followers[numfollowers].name);
strcpy(dname, followers[numfollowers].skinname); // display name, just used for printing succesful stuff or errors later down the line.
// now that the skin name is ready, post process the actual name to turn the underscores into spaces! // now that the skin name is ready, post process the actual name to turn the underscores into spaces!
for (i = 0; followers[numfollowers].name[i]; i++) for (i = 0; followers[numfollowers].name[i]; i++)
@ -3379,14 +3374,14 @@ void readfollower(MYFILE *f)
if (followers[numfollowers].mode < FOLLOWERMODE_FLOAT || followers[numfollowers].mode >= FOLLOWERMODE__MAX) if (followers[numfollowers].mode < FOLLOWERMODE_FLOAT || followers[numfollowers].mode >= FOLLOWERMODE__MAX)
{ {
followers[numfollowers].mode = FOLLOWERMODE_FLOAT; followers[numfollowers].mode = FOLLOWERMODE_FLOAT;
deh_warning("Follower '%s': Value for 'mode' should be between %d and %d.", dname, FOLLOWERMODE_FLOAT, FOLLOWERMODE__MAX-1); deh_warning("Follower '%s': Value for 'mode' should be between %d and %d.", testname, FOLLOWERMODE_FLOAT, FOLLOWERMODE__MAX-1);
} }
#define FALLBACK(field, field2, threshold, set) \ #define FALLBACK(field, field2, threshold, set) \
if ((signed)followers[numfollowers].field < threshold) \ if ((signed)followers[numfollowers].field < threshold) \
{ \ { \
followers[numfollowers].field = set; \ followers[numfollowers].field = set; \
deh_warning("Follower '%s': Value for '%s' is too low! Minimum should be %d. Value was overwritten to %d.", dname, field2, threshold, set); \ deh_warning("Follower '%s': Value for '%s' is too low! Minimum should be %d. Value was overwritten to %d.", testname, field2, threshold, set); \
} \ } \
FALLBACK(dist, "DIST", 0, 0); FALLBACK(dist, "DIST", 0, 0);
@ -3411,7 +3406,7 @@ if (!followers[numfollowers].field) \
{ \ { \
followers[numfollowers].field = fallbackstate ? fallbackstate : S_INVISIBLE; \ followers[numfollowers].field = fallbackstate ? fallbackstate : S_INVISIBLE; \
if (!fallbackstate) \ if (!fallbackstate) \
deh_warning("Follower '%s' is missing state definition for '%s', no idlestate fallback was found", dname, field2); \ deh_warning("Follower '%s' is missing state definition for '%s', no idlestate fallback was found", testname, field2); \
} \ } \
NOSTATE(idlestate, "IDLESTATE"); NOSTATE(idlestate, "IDLESTATE");
@ -3422,7 +3417,7 @@ if (!followers[numfollowers].field) \
NOSTATE(hitconfirmstate, "HITCONFIRMSTATE"); NOSTATE(hitconfirmstate, "HITCONFIRMSTATE");
#undef NOSTATE #undef NOSTATE
CONS_Printf("Added follower '%s'\n", dname); CONS_Printf("Added follower '%s'\n", testname);
if (followers[numfollowers].category < numfollowercategories) if (followers[numfollowers].category < numfollowercategories)
followercategories[followers[numfollowers].category].numincategory++; followercategories[followers[numfollowers].category].numincategory++;
numfollowers++; // add 1 follower numfollowers++; // add 1 follower

View file

@ -453,7 +453,7 @@ void G_WriteDemoExtraData(void)
if (players[i].followerskin == -1) if (players[i].followerskin == -1)
strncpy(name, "None", 16); strncpy(name, "None", 16);
else else
strncpy(name, followers[players[i].followerskin].skinname, 16); strncpy(name, followers[players[i].followerskin].name, 16);
M_Memcpy(demo_p, name, 16); M_Memcpy(demo_p, name, 16);
demo_p += 16; demo_p += 16;
@ -2093,7 +2093,7 @@ void G_BeginRecording(void)
memset(name, 0, 16); memset(name, 0, 16);
if (player->follower) if (player->follower)
strncpy(name, followers[player->followerskin].skinname, 16); strncpy(name, followers[player->followerskin].name, 16);
else else
strncpy(name, "None", 16); // Say we don't have one, then. strncpy(name, "None", 16); // Say we don't have one, then.

View file

@ -31,7 +31,7 @@ INT32 K_FollowerAvailable(const char *name)
for (i = 0; i < numfollowers; i++) for (i = 0; i < numfollowers; i++)
{ {
if (stricmp(followers[i].skinname, name) == 0) if (stricmp(followers[i].name, name) == 0)
return i; return i;
} }
@ -57,7 +57,7 @@ boolean K_SetFollowerByName(INT32 playernum, const char *skinname)
for (i = 0; i < numfollowers; i++) for (i = 0; i < numfollowers; i++)
{ {
// search in the skin list // search in the skin list
if (stricmp(followers[i].skinname, skinname) == 0) if (stricmp(followers[i].name, skinname) == 0)
{ {
K_SetFollowerByNum(playernum, i); K_SetFollowerByNum(playernum, i);
return true; return true;

View file

@ -45,8 +45,7 @@ typedef enum
// //
typedef struct follower_s typedef struct follower_s
{ {
char skinname[SKINNAMESIZE+1]; // Skin Name. This is what to refer to when asking the commands anything. char name[SKINNAMESIZE+1]; // Skin Name. This is what to refer to when asking the commands anything..
char name[SKINNAMESIZE+1]; // Name. This is used for the menus. We'll just follow the same rules as skins for this.
char icon[8+1]; // Lump names are only 8 characters. (+1 for \0) char icon[8+1]; // Lump names are only 8 characters. (+1 for \0)
UINT8 category; // Category UINT8 category; // Category

View file

@ -3093,9 +3093,6 @@ static void M_MPConfirmCharacterSelection(void)
UINT8 i; UINT8 i;
INT16 col; INT16 col;
char commandnames[][MAXSTRINGLENGTH] = { "skin ", "skin2 ", "skin3 ", "skin4 "};
// ^ laziness 100 (we append a space directly so that we don't have to do it later too!!!!)
for (i = 0; i < splitscreen +1; i++) for (i = 0; i < splitscreen +1; i++)
{ {
char cmd[MAXSTRINGLENGTH]; char cmd[MAXSTRINGLENGTH];
@ -3106,7 +3103,10 @@ static void M_MPConfirmCharacterSelection(void)
CV_StealthSetValue(&cv_playercolor[i], col); CV_StealthSetValue(&cv_playercolor[i], col);
// follower // follower
CV_StealthSetValue(&cv_follower[i], setup_player[i].followern); if (setup_player[i].followern < 0)
CV_StealthSet(&cv_follower[i], "None");
else
CV_StealthSet(&cv_follower[i], followers[setup_player[i].followern].name);
// follower color // follower color
CV_StealthSetValue(&cv_followercolor[i], setup_player[i].followercolor); CV_StealthSetValue(&cv_followercolor[i], setup_player[i].followercolor);
@ -3117,8 +3117,8 @@ static void M_MPConfirmCharacterSelection(void)
// This is a hack to make sure we call Skin[x]_OnChange afterwards // This is a hack to make sure we call Skin[x]_OnChange afterwards
CV_StealthSetValue(&cv_skin[i], -1); CV_StealthSetValue(&cv_skin[i], -1);
strcpy(cmd, commandnames[i]); strcpy(cmd, cv_skin[i].name);
strcat(cmd, skins[setup_player[i].skin].name); strcat(cmd, va(" %s", skins[setup_player[i].skin].name));
COM_ImmedExecute(cmd); COM_ImmedExecute(cmd);
} }
@ -3173,7 +3173,7 @@ void M_CharacterSelectTick(void)
optionsmenu.profile->color = setup_player[0].color; optionsmenu.profile->color = setup_player[0].color;
// save follower // save follower
strcpy(optionsmenu.profile->follower, followers[setup_player[0].followern].skinname); strcpy(optionsmenu.profile->follower, followers[setup_player[0].followern].name);
optionsmenu.profile->followercolor = setup_player[0].followercolor; optionsmenu.profile->followercolor = setup_player[0].followercolor;
// reset setup_player // reset setup_player
@ -3190,7 +3190,10 @@ void M_CharacterSelectTick(void)
CV_StealthSet(&cv_skin[i], skins[setup_player[i].skin].name); CV_StealthSet(&cv_skin[i], skins[setup_player[i].skin].name);
CV_StealthSetValue(&cv_playercolor[i], setup_player[i].color); CV_StealthSetValue(&cv_playercolor[i], setup_player[i].color);
CV_StealthSetValue(&cv_follower[i], setup_player[i].followern); if (setup_player[i].followern < 0)
CV_StealthSet(&cv_follower[i], "None");
else
CV_StealthSet(&cv_follower[i], followers[setup_player[i].followern].name);
CV_StealthSetValue(&cv_followercolor[i], setup_player[i].followercolor); CV_StealthSetValue(&cv_followercolor[i], setup_player[i].followercolor);
} }