Merge branch 'follower-fixes' into 'master'

Followers and Character Select improvements

See merge request KartKrew/Kart!764
This commit is contained in:
Sal 2022-11-14 21:30:03 +00:00
commit e8a6aa7540
12 changed files with 632 additions and 395 deletions

View file

@ -2528,6 +2528,11 @@ void CL_ClearPlayer(INT32 playernum)
{ {
int i; int i;
if (players[playernum].follower)
{
K_RemoveFollower(&players[playernum]);
}
if (players[playernum].mo) if (players[playernum].mo)
{ {
P_RemoveMobj(players[playernum].mo); P_RemoveMobj(players[playernum].mo);

View file

@ -295,10 +295,10 @@ consvar_t cv_follower[MAXSPLITSCREENPLAYERS] = {
// player's follower colors... Also saved... // player's follower colors... Also saved...
consvar_t cv_followercolor[MAXSPLITSCREENPLAYERS] = { consvar_t cv_followercolor[MAXSPLITSCREENPLAYERS] = {
CVAR_INIT ("followercolor", "1", CV_SAVE|CV_CALL|CV_NOINIT, Followercolor_cons_t, Followercolor_OnChange), CVAR_INIT ("followercolor", "Match", CV_SAVE|CV_CALL|CV_NOINIT, Followercolor_cons_t, Followercolor_OnChange),
CVAR_INIT ("followercolor2", "1", CV_SAVE|CV_CALL|CV_NOINIT, Followercolor_cons_t, Followercolor2_OnChange), CVAR_INIT ("followercolor2", "Match", CV_SAVE|CV_CALL|CV_NOINIT, Followercolor_cons_t, Followercolor2_OnChange),
CVAR_INIT ("followercolor3", "1", CV_SAVE|CV_CALL|CV_NOINIT, Followercolor_cons_t, Followercolor3_OnChange), CVAR_INIT ("followercolor3", "Match", CV_SAVE|CV_CALL|CV_NOINIT, Followercolor_cons_t, Followercolor3_OnChange),
CVAR_INIT ("followercolor4", "1", CV_SAVE|CV_CALL|CV_NOINIT, Followercolor_cons_t, Followercolor4_OnChange) CVAR_INIT ("followercolor4", "Match", CV_SAVE|CV_CALL|CV_NOINIT, Followercolor_cons_t, Followercolor4_OnChange)
}; };
// last selected profile, unaccessible cvar only set internally but is saved. // last selected profile, unaccessible cvar only set internally but is saved.
@ -831,27 +831,6 @@ void D_RegisterClientCommands(void)
{ {
INT32 i; INT32 i;
for (i = 0; i < MAXSKINCOLORS; i++)
{
Color_cons_t[i].value = i;
Color_cons_t[i].strvalue = skincolors[i].name;
}
for (i = 2; i < MAXSKINCOLORS; i++)
{
Followercolor_cons_t[i].value = i-2;
Followercolor_cons_t[i].strvalue = skincolors[i-2].name;
}
Followercolor_cons_t[1].value = FOLLOWERCOLOR_MATCH;
Followercolor_cons_t[1].strvalue = "Match"; // Add "Match" option, which will make the follower color match the player's
Followercolor_cons_t[0].value = FOLLOWERCOLOR_OPPOSITE;
Followercolor_cons_t[0].strvalue = "Opposite"; // Add "Opposite" option, ...which is like "Match", but for coloropposite.
Color_cons_t[MAXSKINCOLORS].value = Followercolor_cons_t[MAXSKINCOLORS+2].value = 0;
Color_cons_t[MAXSKINCOLORS].strvalue = Followercolor_cons_t[MAXSKINCOLORS+2].strvalue = NULL;
// Set default player names // Set default player names
// Monster Iestyn (12/08/19): not sure where else I could have actually put this, but oh well // Monster Iestyn (12/08/19): not sure where else I could have actually put this, but oh well
for (i = 0; i < MAXPLAYERS; i++) for (i = 0; i < MAXPLAYERS; i++)
@ -1493,7 +1472,7 @@ static void SendNameAndColor(UINT8 n)
const INT32 playernum = g_localplayers[n]; const INT32 playernum = g_localplayers[n];
player_t *player = &players[playernum]; player_t *player = &players[playernum];
char buf[MAXPLAYERNAME+9]; char buf[MAXPLAYERNAME+12];
char *p; char *p;
if (splitscreen < n) if (splitscreen < n)
@ -1524,14 +1503,11 @@ static void SendNameAndColor(UINT8 n)
if (!cv_followercolor[n].value) if (!cv_followercolor[n].value)
CV_StealthSet(&cv_followercolor[n], "Match"); // set it to "Match". I don't care about your stupidity! CV_StealthSet(&cv_followercolor[n], "Match"); // set it to "Match". I don't care about your stupidity!
// so like, this is sent before we even use anything like cvars or w/e so it's possible that follower is set to a pretty yikes value, so let's fix that before we send garbage that could crash the game:
if (cv_follower[n].value >= numfollowers || cv_follower[n].value < -1)
CV_StealthSet(&cv_follower[n], "-1");
if (!strcmp(cv_playername[n].string, player_names[playernum]) if (!strcmp(cv_playername[n].string, player_names[playernum])
&& cv_playercolor[n].value == player->skincolor && cv_playercolor[n].value == player->skincolor
&& !strcmp(cv_skin[n].string, skins[player->skin].name) && !stricmp(cv_skin[n].string, skins[player->skin].name)
&& cv_follower[n].value == player->followerskin && !stricmp(cv_follower[n].string,
(player->followerskin < 0 ? "None" : followers[player->followerskin].name))
&& cv_followercolor[n].value == player->followercolor) && cv_followercolor[n].value == player->followercolor)
return; return;
@ -1553,31 +1529,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;
} }
@ -1607,12 +1580,20 @@ 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);
WRITEUINT16(p, (UINT16)cv_playercolor[n].value); WRITEUINT16(p, (UINT16)cv_playercolor[n].value);
WRITEUINT8(p, (UINT8)cv_skin[n].value); WRITEUINT8(p, (UINT8)cv_skin[n].value);
WRITESINT8(p, (SINT8)cv_follower[n].value); WRITEINT16(p, (INT16)cv_follower[n].value);
//CONS_Printf("Sending follower id %d\n", (INT16)cv_follower[n].value);
WRITEUINT16(p, (UINT16)cv_followercolor[n].value); WRITEUINT16(p, (UINT16)cv_followercolor[n].value);
SendNetXCmdForPlayer(n, XD_NAMEANDCOLOR, buf, p - buf); SendNetXCmdForPlayer(n, XD_NAMEANDCOLOR, buf, p - buf);
@ -1624,7 +1605,7 @@ static void Got_NameAndColor(UINT8 **cp, INT32 playernum)
char name[MAXPLAYERNAME+1]; char name[MAXPLAYERNAME+1];
UINT16 color, followercolor; UINT16 color, followercolor;
UINT8 skin; UINT8 skin;
SINT8 follower; INT16 follower;
SINT8 localplayer = -1; SINT8 localplayer = -1;
UINT8 i; UINT8 i;
@ -1653,7 +1634,8 @@ static void Got_NameAndColor(UINT8 **cp, INT32 playernum)
p->availabilities = READUINT32(*cp); p->availabilities = READUINT32(*cp);
color = READUINT16(*cp); color = READUINT16(*cp);
skin = READUINT8(*cp); skin = READUINT8(*cp);
follower = READSINT8(*cp); follower = READINT16(*cp);
//CONS_Printf("Recieved follower id %d\n", follower);
followercolor = READUINT16(*cp); followercolor = READUINT16(*cp);
// set name // set name
@ -6079,6 +6061,7 @@ static void Name_OnChange(void)
{ {
CONS_Alert(CONS_NOTICE, M_GetText("You may not change your name when chat is muted.\n")); CONS_Alert(CONS_NOTICE, M_GetText("You may not change your name when chat is muted.\n"));
CV_StealthSet(&cv_playername[0], player_names[consoleplayer]); CV_StealthSet(&cv_playername[0], player_names[consoleplayer]);
return;
} }
else else
SendNameAndColor(0); SendNameAndColor(0);
@ -6119,207 +6102,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
@ -6339,7 +6171,9 @@ static void Skin_OnChange(void)
} }
if (CanChangeSkinWhilePlaying(consoleplayer)) if (CanChangeSkinWhilePlaying(consoleplayer))
{
SendNameAndColor(0); SendNameAndColor(0);
}
else else
{ {
CONS_Alert(CONS_NOTICE, M_GetText("You can't change your skin at the moment.\n")); CONS_Alert(CONS_NOTICE, M_GetText("You can't change your skin at the moment.\n"));

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];
@ -3139,10 +3139,9 @@ void readfollower(MYFILE *f)
INT32 res; INT32 res;
INT32 i; INT32 i;
if (numfollowers > MAXSKINS) if (numfollowers >= MAXSKINS)
{ {
deh_warning("Error: Too many followers, cannot add anymore.\n"); I_Error("Out of Followers\nLoad less addons to fix this.");
return;
} }
s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL); s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL);
@ -3161,8 +3160,9 @@ void readfollower(MYFILE *f)
followers[numfollowers].bobspeed = TICRATE*2; followers[numfollowers].bobspeed = TICRATE*2;
followers[numfollowers].bobamp = 4*FRACUNIT; followers[numfollowers].bobamp = 4*FRACUNIT;
followers[numfollowers].hitconfirmtime = TICRATE; followers[numfollowers].hitconfirmtime = TICRATE;
followers[numfollowers].defaultcolor = SKINCOLOR_GREEN; followers[numfollowers].defaultcolor = FOLLOWERCOLOR_MATCH;
strcpy(followers[numfollowers].icon, "M_NORANK"); followers[numfollowers].category = UINT8_MAX;
strcpy(followers[numfollowers].icon, "MISSING");
do do
{ {
@ -3201,6 +3201,23 @@ void readfollower(MYFILE *f)
strcpy(followers[numfollowers].icon, word2); strcpy(followers[numfollowers].icon, word2);
nameset = true; nameset = true;
} }
else if (fastcmp(word, "CATEGORY"))
{
INT32 j;
for (j = 0; j < numfollowercategories; j++)
{
if (!stricmp(followercategories[j].name, word2))
{
followers[numfollowers].category = j;
break;
}
}
if (j == numfollowercategories)
{
deh_warning("Follower %d: unknown follower category '%s'", numfollowers, word2);
}
}
else if (fastcmp(word, "MODE")) else if (fastcmp(word, "MODE"))
{ {
if (word2) if (word2)
@ -3215,7 +3232,20 @@ void readfollower(MYFILE *f)
} }
else if (fastcmp(word, "DEFAULTCOLOR")) else if (fastcmp(word, "DEFAULTCOLOR"))
{ {
followers[numfollowers].defaultcolor = get_number(word2); INT32 j;
for (j = 0; j < numskincolors +2; j++) // +2 because of Match and Opposite
{
if (!stricmp(Followercolor_cons_t[j].strvalue, word2))
{
followers[numfollowers].defaultcolor = Followercolor_cons_t[j].value;
break;
}
}
if (j == numskincolors+2)
{
deh_warning("Follower %d: unknown follower color '%s'", numfollowers, word2);
}
} }
else if (fastcmp(word, "SCALE")) else if (fastcmp(word, "SCALE"))
{ {
@ -3318,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
{ {
@ -3333,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++)
@ -3349,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);
@ -3373,13 +3398,6 @@ if ((signed)followers[numfollowers].field < threshold) \
#undef FALLBACK #undef FALLBACK
// Special case for color I suppose
if (followers[numfollowers].defaultcolor > (unsigned)(numskincolors-1))
{
followers[numfollowers].defaultcolor = SKINCOLOR_GREEN;
deh_warning("Follower \'%s\': Value for 'color' should be between 1 and %d.\n", dname, numskincolors-1);
}
// also check if we forgot states. If we did, we will set any missing state to the follower's idlestate. // also check if we forgot states. If we did, we will set any missing state to the follower's idlestate.
// Print a warning in case we don't have a fallback and set the state to S_INVISIBLE (rather than S_NULL) if unavailable. // Print a warning in case we don't have a fallback and set the state to S_INVISIBLE (rather than S_NULL) if unavailable.
@ -3388,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");
@ -3399,11 +3417,83 @@ 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)
followercategories[followers[numfollowers].category].numincategory++;
numfollowers++; // add 1 follower numfollowers++; // add 1 follower
Z_Free(s); Z_Free(s);
} }
void readfollowercategory(MYFILE *f)
{
char *s;
char *word, *word2;
char *tmp;
boolean nameset;
if (numfollowercategories == MAXFOLLOWERCATEGORIES)
{
I_Error("Out of Follower categories\nLoad less addons to fix this.");
}
s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL);
// Ready the default variables for followers. We will overwrite them as we go! We won't set the name or states RIGHT HERE as this is handled down instead.
strcpy(followercategories[numfollowercategories].icon, "MISSING");
followercategories[numfollowercategories].numincategory = 0;
do
{
if (myfgets(s, MAXLINELEN, f))
{
if (s[0] == '\n')
break;
tmp = strchr(s, '#');
if (tmp)
*tmp = '\0';
if (s == tmp)
continue; // Skip comment lines, but don't break.
word = strtok(s, " ");
if (word)
strupr(word);
else
break;
word2 = strtok(NULL, " = ");
if (!word2)
break;
if (word2[strlen(word2)-1] == '\n')
word2[strlen(word2)-1] = '\0';
if (fastcmp(word, "NAME"))
{
strcpy(followercategories[numfollowercategories].name, word2);
nameset = true;
}
else if (fastcmp(word, "ICON"))
{
strcpy(followercategories[numfollowercategories].icon, word2);
nameset = true;
}
}
} while (!myfeof(f)); // finish when the line is empty
if (!nameset)
{
// well this is problematic.
strcpy(followercategories[numfollowercategories].name, va("Followercategory%d", numfollowercategories)); // this is lazy, so what
}
CONS_Printf("Added follower category '%s'\n", followercategories[numfollowercategories].name);
numfollowercategories++; // add 1 follower
Z_Free(s);
}
void readweather(MYFILE *f, INT32 num) void readweather(MYFILE *f, INT32 num)
{ {
char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL); char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL);

View file

@ -82,6 +82,7 @@ void clear_conditionsets(void);
void readcupheader(MYFILE *f, cupheader_t *cup); void readcupheader(MYFILE *f, cupheader_t *cup);
void readfollower(MYFILE *f); void readfollower(MYFILE *f);
void readfollowercategory(MYFILE *f);
preciptype_t get_precip(const char *word); preciptype_t get_precip(const char *word);
void readweather(MYFILE *f, INT32 num); void readweather(MYFILE *f, INT32 num);

View file

@ -235,6 +235,12 @@ static void DEH_LoadDehackedFile(MYFILE *f, boolean mainfile)
readfollower(f); readfollower(f);
continue; continue;
} }
else if (fastcmp(word, "FOLLOWERCATEGORY"))
{
// This is not a major mod.
readfollowercategory(f);
continue;
}
word2 = strtok(NULL, " "); word2 = strtok(NULL, " ");
if (word2) { if (word2) {

View file

@ -313,11 +313,11 @@ void G_ReadDemoExtraData(void)
demo_p += 16; demo_p += 16;
for (i = 0; i < numskincolors +2; i++) // +2 because of Match and Opposite for (i = 0; i < numskincolors +2; i++) // +2 because of Match and Opposite
{ {
if (!stricmp(Followercolor_cons_t[i].strvalue, name)) if (!stricmp(Followercolor_cons_t[i].strvalue, name))
{ {
players[p].followercolor = i; players[p].followercolor = Followercolor_cons_t[i].value;
break; break;
} }
} }
} }
if (extradata & DXD_PLAYSTATE) if (extradata & DXD_PLAYSTATE)
@ -407,7 +407,7 @@ void G_ReadDemoExtraData(void)
void G_WriteDemoExtraData(void) void G_WriteDemoExtraData(void)
{ {
INT32 i; INT32 i, j;
char name[16]; char name[16];
for (i = 0; i < MAXPLAYERS; i++) for (i = 0; i < MAXPLAYERS; i++)
@ -453,13 +453,18 @@ 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;
// write follower color // write follower color
memset(name, 0, 16); memset(name, 0, 16);
strncpy(name, Followercolor_cons_t[(UINT16)(players[i].followercolor+2)].strvalue, 16); // Not KartColor_Names because followercolor has extra values such as "Match" for (j = (numskincolors+2)-1; j > 0; j--)
{
if (Followercolor_cons_t[j].value == players[i].followercolor)
break;
}
strncpy(name, Followercolor_cons_t[j].strvalue, 16); // Not KartColor_Names because followercolor has extra values such as "Match"
M_Memcpy(demo_p,name,16); M_Memcpy(demo_p,name,16);
demo_p += 16; demo_p += 16;
@ -1951,7 +1956,7 @@ void G_RecordMetal(void)
void G_BeginRecording(void) void G_BeginRecording(void)
{ {
UINT8 i, p; UINT8 i, j, p;
char name[MAXCOLORNAME+1]; char name[MAXCOLORNAME+1];
player_t *player = &players[consoleplayer]; player_t *player = &players[consoleplayer];
@ -2088,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.
@ -2097,7 +2102,12 @@ void G_BeginRecording(void)
// Save follower's colour // Save follower's colour
memset(name, 0, 16); memset(name, 0, 16);
strncpy(name, Followercolor_cons_t[(UINT16)(player->followercolor+2)].strvalue, 16); // Not KartColor_Names because followercolor has extra values such as "Match" for (j = (numskincolors+2)-1; j > 0; j--)
{
if (Followercolor_cons_t[j].value == players[i].followercolor)
break;
}
strncpy(name, Followercolor_cons_t[j].strvalue, 16); // Not KartColor_Names because followercolor has extra values such as "Match"
M_Memcpy(demo_p, name, 16); M_Memcpy(demo_p, name, 16);
demo_p += 16; demo_p += 16;
@ -3073,11 +3083,11 @@ void G_DoPlayDemo(char *defdemoname)
demo_p += 16; demo_p += 16;
for (i = 0; i < numskincolors +2; i++) // +2 because of Match and Opposite for (i = 0; i < numskincolors +2; i++) // +2 because of Match and Opposite
{ {
if (!stricmp(Followercolor_cons_t[i].strvalue, color)) if (!stricmp(Followercolor_cons_t[i].strvalue, color))
{ {
players[p].followercolor = i; players[p].followercolor = Followercolor_cons_t[i].value;
break; break;
} }
} }
// Score, since Kart uses this to determine where you start on the map // Score, since Kart uses this to determine where you start on the map

View file

@ -4263,8 +4263,26 @@ void G_EndGame(void)
// Sets a tad of default info we need. // Sets a tad of default info we need.
void G_LoadGameSettings(void) void G_LoadGameSettings(void)
{ {
INT32 i;
// initialize free sfx slots for skin sounds // initialize free sfx slots for skin sounds
S_InitRuntimeSounds(); S_InitRuntimeSounds();
// Prepare skincolor material.
for (i = 0; i < MAXSKINCOLORS; i++)
{
Color_cons_t[i].value = Followercolor_cons_t[i+2].value = i;
Color_cons_t[i].strvalue = Followercolor_cons_t[i+2].strvalue = skincolors[i].name;
}
Followercolor_cons_t[1].value = FOLLOWERCOLOR_MATCH;
Followercolor_cons_t[1].strvalue = "Match"; // Add "Match" option, which will make the follower color match the player's
Followercolor_cons_t[0].value = FOLLOWERCOLOR_OPPOSITE;
Followercolor_cons_t[0].strvalue = "Opposite"; // Add "Opposite" option, ...which is like "Match", but for coloropposite.
Color_cons_t[MAXSKINCOLORS].value = Followercolor_cons_t[MAXSKINCOLORS+2].value = 0;
Color_cons_t[MAXSKINCOLORS].strvalue = Followercolor_cons_t[MAXSKINCOLORS+2].strvalue = NULL;
} }
#define GD_VERSIONCHECK 0xBA5ED444 // Change every major version, as usual #define GD_VERSIONCHECK 0xBA5ED444 // Change every major version, as usual

View file

@ -15,6 +15,9 @@
INT32 numfollowers = 0; INT32 numfollowers = 0;
follower_t followers[MAXSKINS]; follower_t followers[MAXSKINS];
INT32 numfollowercategories;
followercategory_t followercategories[MAXFOLLOWERCATEGORIES];
CV_PossibleValue_t Followercolor_cons_t[MAXSKINCOLORS+3]; // +3 to account for "Match", "Opposite" & NULL CV_PossibleValue_t Followercolor_cons_t[MAXSKINCOLORS+3]; // +3 to account for "Match", "Opposite" & NULL
/*-------------------------------------------------- /*--------------------------------------------------
@ -28,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;
} }
@ -54,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;
@ -74,6 +77,31 @@ boolean K_SetFollowerByName(INT32 playernum, const char *skinname)
return false; return false;
} }
/*--------------------------------------------------
void K_RemoveFollower(player_t *player)
See header file for description.
--------------------------------------------------*/
void K_RemoveFollower(player_t *player)
{
mobj_t *bub, *tmp;
if (player->follower && !P_MobjWasRemoved(player->follower)) // this is also called when we change colour so don't respawn the follower unless we changed skins
{
// Remove follower's possible hnext list (bubble)
bub = player->follower->hnext;
while (bub && !P_MobjWasRemoved(bub))
{
tmp = bub->hnext;
P_RemoveMobj(bub);
bub = tmp;
}
P_RemoveMobj(player->follower);
P_SetTarget(&player->follower, NULL);
}
}
/*-------------------------------------------------- /*--------------------------------------------------
void K_SetFollowerByNum(INT32 playernum, INT32 skinnum) void K_SetFollowerByNum(INT32 playernum, INT32 skinnum)
@ -82,8 +110,6 @@ boolean K_SetFollowerByName(INT32 playernum, const char *skinname)
void K_SetFollowerByNum(INT32 playernum, INT32 skinnum) void K_SetFollowerByNum(INT32 playernum, INT32 skinnum)
{ {
player_t *player = &players[playernum]; player_t *player = &players[playernum];
mobj_t *bub;
mobj_t *tmp;
player->followerready = true; // we are ready to perform follower related actions in the player thinker, now. player->followerready = true; // we are ready to perform follower related actions in the player thinker, now.
@ -94,21 +120,8 @@ void K_SetFollowerByNum(INT32 playernum, INT32 skinnum)
However, we will despawn it right here if there's any to make it easy for the player thinker to replace it or delete it. However, we will despawn it right here if there's any to make it easy for the player thinker to replace it or delete it.
*/ */
if (player->follower && skinnum != player->followerskin) // this is also called when we change colour so don't respawn the follower unless we changed skins if (skinnum != player->followerskin)
{ K_RemoveFollower(player);
// Remove follower's possible hnext list (bubble)
bub = player->follower->hnext;
while (bub && !P_MobjWasRemoved(bub))
{
tmp = bub->hnext;
P_RemoveMobj(bub);
bub = tmp;
}
P_RemoveMobj(player->follower);
P_SetTarget(&player->follower, NULL);
}
player->followerskin = skinnum; player->followerskin = skinnum;
@ -253,18 +266,16 @@ void K_HandleFollower(player_t *player)
{ {
//CONS_Printf("Follower skin invlaid. Setting to -1.\n"); //CONS_Printf("Follower skin invlaid. Setting to -1.\n");
player->followerskin = -1; player->followerskin = -1;
return;
} }
// don't do anything if we can't have a follower to begin with. // don't do anything if we can't have a follower to begin with.
// (It gets removed under those conditions) // (It gets removed under those conditions)
if (player->spectator) if (player->spectator || player->followerskin < 0)
{
return;
}
if (player->followerskin < 0)
{ {
if (player->follower)
{
K_RemoveFollower(player);
}
return; return;
} }

View file

@ -45,10 +45,11 @@ 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
skincolornum_t defaultcolor; // default color for menus. skincolornum_t defaultcolor; // default color for menus.
followermode_t mode; // Follower behavior modifier. followermode_t mode; // Follower behavior modifier.
@ -85,6 +86,18 @@ typedef struct follower_s
extern INT32 numfollowers; extern INT32 numfollowers;
extern follower_t followers[MAXSKINS]; extern follower_t followers[MAXSKINS];
#define MAXFOLLOWERCATEGORIES 32
typedef struct followercategory_s
{
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)
UINT8 numincategory;
} followercategory_t;
extern INT32 numfollowercategories;
extern followercategory_t followercategories[MAXFOLLOWERCATEGORIES];
/*-------------------------------------------------- /*--------------------------------------------------
INT32 K_FollowerAvailable(const char *name) INT32 K_FollowerAvailable(const char *name)
@ -168,5 +181,19 @@ UINT16 K_GetEffectiveFollowerColor(UINT16 followercolor, UINT16 playercolor);
void K_HandleFollower(player_t *player); void K_HandleFollower(player_t *player);
/*--------------------------------------------------
void K_RemoveFollower(player_t *player)
Removes Follower object
Input Arguments:-
player - The player who we want to remove the follower of.
Return:-
None
--------------------------------------------------*/
void K_RemoveFollower(player_t *player);
#endif // __K_FOLLOWER__ #endif // __K_FOLLOWER__

View file

@ -599,6 +599,7 @@ typedef enum
CSSTEP_CHARS, CSSTEP_CHARS,
CSSTEP_ALTS, CSSTEP_ALTS,
CSSTEP_COLORS, CSSTEP_COLORS,
CSSTEP_FOLLOWERCATEGORY,
CSSTEP_FOLLOWER, CSSTEP_FOLLOWER,
CSSTEP_FOLLOWERCOLORS, CSSTEP_FOLLOWERCOLORS,
CSSTEP_READY CSSTEP_READY
@ -614,6 +615,7 @@ typedef struct setup_player_s
UINT8 delay; UINT8 delay;
UINT16 color; UINT16 color;
UINT8 mdepth; UINT8 mdepth;
boolean hitlag;
// Hack, save player 1's original device even if they init charsel with keyboard. // Hack, save player 1's original device even if they init charsel with keyboard.
// If they play ALONE, allow them to retain that original device, otherwise, ignore this. // If they play ALONE, allow them to retain that original device, otherwise, ignore this.
@ -622,7 +624,8 @@ typedef struct setup_player_s
UINT8 changeselect; UINT8 changeselect;
INT32 followern; INT16 followercategory;
INT16 followern;
UINT16 followercolor; UINT16 followercolor;
tic_t follower_tics; tic_t follower_tics;
tic_t follower_timer; tic_t follower_timer;

View file

@ -969,7 +969,8 @@ static void M_DrawCharSelectCircle(setup_player_t *p, INT16 x, INT16 y)
{ {
angle_t angamt = ANGLE_MAX; angle_t angamt = ANGLE_MAX;
UINT16 i, numoptions = 0; UINT16 i, numoptions = 0;
UINT16 l = 0, r = 0; INT16 l = 0, r = 0;
INT16 subtractcheck;
switch (p->mdepth) switch (p->mdepth)
{ {
@ -979,8 +980,11 @@ static void M_DrawCharSelectCircle(setup_player_t *p, INT16 x, INT16 y)
case CSSTEP_COLORS: case CSSTEP_COLORS:
numoptions = nummenucolors; numoptions = nummenucolors;
break; break;
case CSSTEP_FOLLOWERCATEGORY:
numoptions = numfollowercategories+1;
break;
case CSSTEP_FOLLOWER: case CSSTEP_FOLLOWER:
numoptions = numfollowers+1; numoptions = followercategories[p->followercategory].numincategory;
break; break;
case CSSTEP_FOLLOWERCOLORS: case CSSTEP_FOLLOWERCOLORS:
numoptions = nummenucolors+2; numoptions = nummenucolors+2;
@ -994,17 +998,19 @@ static void M_DrawCharSelectCircle(setup_player_t *p, INT16 x, INT16 y)
return; return;
} }
subtractcheck = 1 ^ (numoptions & 1);
angamt /= numoptions; angamt /= numoptions;
for (i = 0; i < numoptions; i++) for (i = 0; i < numoptions; i++)
{ {
fixed_t cx = x << FRACBITS, cy = y << FRACBITS; fixed_t cx = x << FRACBITS, cy = y << FRACBITS;
boolean subtract = (i & 1); boolean subtract = (i & 1) == subtractcheck;
angle_t ang = ((i+1)/2) * angamt; angle_t ang = ((i+1)/2) * angamt;
patch_t *patch = NULL; patch_t *patch = NULL;
UINT8 *colormap = NULL; UINT8 *colormap = NULL;
fixed_t radius = 28<<FRACBITS; fixed_t radius = 28<<FRACBITS;
UINT16 n = 0; INT16 n = 0;
switch (p->mdepth) switch (p->mdepth)
{ {
@ -1017,7 +1023,7 @@ static void M_DrawCharSelectCircle(setup_player_t *p, INT16 x, INT16 y)
n -= ((i+1)/2); n -= ((i+1)/2);
else else
n += ((i+1)/2); n += ((i+1)/2);
n %= numoptions; n = (n + numoptions) % numoptions;
skin = setup_chargrid[p->gridx][p->gridy].skinlist[n]; skin = setup_chargrid[p->gridx][p->gridy].skinlist[n];
patch = faceprefix[skin][FACE_RANK]; patch = faceprefix[skin][FACE_RANK];
@ -1061,16 +1067,16 @@ static void M_DrawCharSelectCircle(setup_player_t *p, INT16 x, INT16 y)
break; break;
} }
case CSSTEP_FOLLOWER: case CSSTEP_FOLLOWERCATEGORY:
{ {
follower_t *fl = NULL; followercategory_t *fc = NULL;
n = (p->followern + 1) + numoptions/2; n = (p->followercategory + 1) + numoptions/2;
if (subtract) if (subtract)
n -= ((i+1)/2); n -= ((i+1)/2);
else else
n += ((i+1)/2); n += ((i+1)/2);
n %= numoptions; n = (n + numoptions) % numoptions;
if (n == 0) if (n == 0)
{ {
@ -1078,11 +1084,71 @@ static void M_DrawCharSelectCircle(setup_player_t *p, INT16 x, INT16 y)
} }
else else
{ {
fl = &followers[n - 1]; fc = &followercategories[n - 1];
patch = W_CachePatchName(fc->icon, PU_CACHE);
}
radius = 24<<FRACBITS;
cx -= (SHORT(patch->width) << FRACBITS) >> 1;
cy -= (SHORT(patch->height) << FRACBITS) >> 1;
break;
}
case CSSTEP_FOLLOWER:
{
follower_t *fl = NULL;
INT16 startfollowern = p->followern;
if (i == 0)
{
n = p->followern;
r = (numoptions+1)/2;
while (r)
{
n--;
if (n < 0)
n = numfollowers-1;
if (n == startfollowern)
break;
if (followers[n].category == p->followercategory)
r--;
}
l = r = n;
}
else if (subtract)
{
do
{
l--;
if (l < 0)
l = numfollowers-1;
if (l == startfollowern)
break;
}
while (followers[l].category != p->followercategory);
n = l;
}
else
{
do
{
r++;
if (r >= numfollowers)
r = 0;
if (r == startfollowern)
break;
}
while (followers[r].category != p->followercategory);
n = r;
}
{
fl = &followers[n];
patch = W_CachePatchName(fl->icon, PU_CACHE); patch = W_CachePatchName(fl->icon, PU_CACHE);
colormap = R_GetTranslationColormap(TC_DEFAULT, colormap = R_GetTranslationColormap(TC_DEFAULT,
K_GetEffectiveFollowerColor(p->followercolor, p->color), K_GetEffectiveFollowerColor(fl->defaultcolor, p->color),
GTC_MENUCACHE GTC_MENUCACHE
); );
} }
@ -1142,7 +1208,12 @@ static void M_DrawCharSelectCircle(setup_player_t *p, INT16 x, INT16 y)
ang = (signed)(ang - (angamt/2)); ang = (signed)(ang - (angamt/2));
if (p->rotate) if (p->rotate)
ang = (signed)(ang + ((angamt / CSROTATETICS) * p->rotate)); {
SINT8 rotate = p->rotate;
if ((p->hitlag == true) && (setup_animcounter & 1))
rotate = -rotate;
ang = (signed)(ang + ((angamt / CSROTATETICS) * rotate));
}
cx += FixedMul(radius, FINECOSINE(ang >> ANGLETOFINESHIFT)); cx += FixedMul(radius, FINECOSINE(ang >> ANGLETOFINESHIFT));
cy -= FixedMul(radius, FINESINE(ang >> ANGLETOFINESHIFT)) / 3; cy -= FixedMul(radius, FINESINE(ang >> ANGLETOFINESHIFT)) / 3;
@ -1154,41 +1225,40 @@ static void M_DrawCharSelectCircle(setup_player_t *p, INT16 x, INT16 y)
} }
// returns false if the character couldn't be rendered // returns false if the character couldn't be rendered
static boolean M_DrawCharacterSprite(INT16 x, INT16 y, INT16 skin, INT32 addflags, UINT8 *colormap) static boolean M_DrawCharacterSprite(INT16 x, INT16 y, INT16 skin, boolean charflip, boolean animate, INT32 addflags, UINT8 *colormap)
{ {
UINT8 spr; UINT8 spr;
spritedef_t *sprdef; spritedef_t *sprdef;
spriteframe_t *sprframe; spriteframe_t *sprframe;
patch_t *sprpatch; patch_t *sprpatch;
UINT8 rotation = (charflip ? 1 : 7);
UINT32 frame = animate ? setup_animcounter : 0;
UINT32 flags = 0; spr = P_GetSkinSprite2(&skins[skin], SPR2_STIN, NULL);
UINT32 frame;
spr = P_GetSkinSprite2(&skins[skin], SPR2_FSTN, NULL);
sprdef = &skins[skin].sprites[spr]; sprdef = &skins[skin].sprites[spr];
if (!sprdef->numframes) // No frames ?? if (!sprdef->numframes) // No frames ??
return false; // Can't render! return false; // Can't render!
frame = states[S_KART_FAST].frame & FF_FRAMEMASK; frame %= sprdef->numframes;
if (frame >= sprdef->numframes) // Walking animation missing
frame = 0; // Try to use standing frame
sprframe = &sprdef->spriteframes[frame]; sprframe = &sprdef->spriteframes[frame];
sprpatch = W_CachePatchNum(sprframe->lumppat[1], PU_CACHE); sprpatch = W_CachePatchNum(sprframe->lumppat[rotation], PU_CACHE);
if (sprframe->flip & 1) // Only for first sprite if (sprframe->flip & (1<<rotation)) // Only for first sprite
flags |= V_FLIP; // This sprite is left/right flipped! {
addflags ^= V_FLIP; // This sprite is left/right flipped!
}
if (skins[skin].flags & SF_HIRES) if (skins[skin].flags & SF_HIRES)
{ {
V_DrawFixedPatch(x<<FRACBITS, V_DrawFixedPatch(x<<FRACBITS,
y<<FRACBITS, y<<FRACBITS,
skins[skin].highresscale, skins[skin].highresscale,
flags, sprpatch, colormap); addflags, sprpatch, colormap);
} }
else else
V_DrawMappedPatch(x, y, addflags|flags, sprpatch, colormap); V_DrawMappedPatch(x, y, addflags, sprpatch, colormap);
return true; return true;
} }
@ -1196,7 +1266,7 @@ static boolean M_DrawCharacterSprite(INT16 x, INT16 y, INT16 skin, INT32 addflag
// Returns false is the follower shouldn't be rendered. // 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. // '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. // 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, INT32 addflags, UINT16 color, setup_player_t *p) static boolean M_DrawFollowerSprite(INT16 x, INT16 y, INT32 num, boolean charflip, INT32 addflags, UINT16 color, setup_player_t *p)
{ {
spritedef_t *sprdef; spritedef_t *sprdef;
@ -1207,6 +1277,7 @@ static boolean M_DrawFollowerSprite(INT16 x, INT16 y, INT32 num, INT32 addflags,
UINT32 useframe; UINT32 useframe;
follower_t fl; follower_t fl;
UINT8 *colormap = NULL; UINT8 *colormap = NULL;
UINT8 rotation = (charflip ? 1 : 7);
if (p != NULL) if (p != NULL)
followernum = p->followern; followernum = p->followern;
@ -1238,14 +1309,11 @@ static boolean M_DrawFollowerSprite(INT16 x, INT16 y, INT32 num, INT32 addflags,
useframe = 0; // frame doesn't exist, we went beyond it... what? useframe = 0; // frame doesn't exist, we went beyond it... what?
sprframe = &sprdef->spriteframes[useframe]; sprframe = &sprdef->spriteframes[useframe];
patch = W_CachePatchNum(sprframe->lumppat[1], PU_CACHE); patch = W_CachePatchNum(sprframe->lumppat[rotation], PU_CACHE);
if (sprframe->flip & 2) // Only for first sprite if (sprframe->flip & (1<<rotation)) // Only for first sprite
{ {
if (addflags & V_FLIP) addflags ^= V_FLIP; // This sprite is left/right flipped!
addflags &= ~V_FLIP;
else
addflags |= V_FLIP; // This sprite is left/right flipped!
} }
fixed_t sine = 0; fixed_t sine = 0;
@ -1253,35 +1321,33 @@ static boolean M_DrawFollowerSprite(INT16 x, INT16 y, INT32 num, INT32 addflags,
if (p != NULL) if (p != NULL)
{ {
sine = FixedMul(fl.bobamp, FINESINE(((FixedMul(4 * M_TAU_FIXED, fl.bobspeed) * p->follower_timer)>>ANGLETOFINESHIFT) & FINEMASK)); sine = FixedMul(fl.bobamp, FINESINE(((FixedMul(4 * M_TAU_FIXED, fl.bobspeed) * p->follower_timer)>>ANGLETOFINESHIFT) & FINEMASK));
color = K_GetEffectiveFollowerColor(p->followercolor, p->color); color = K_GetEffectiveFollowerColor(
(p->mdepth < CSSTEP_FOLLOWERCOLORS) ? fl.defaultcolor : p->followercolor,
p->color);
} }
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.zoffs, fl.scale, addflags, patch, colormap); V_DrawFixedPatch((x*FRACUNIT), ((y-12)*FRACUNIT) + sine, fl.scale, addflags, patch, colormap);
return true; return true;
} }
static void M_DrawCharSelectSprite(UINT8 num, INT16 x, INT16 y) static void M_DrawCharSelectSprite(UINT8 num, INT16 x, INT16 y, boolean charflip)
{ {
setup_player_t *p = &setup_player[num]; setup_player_t *p = &setup_player[num];
UINT8 cnum = p->clonenum; UINT8 color;
UINT8 *colormap;
// for p1 alone don't try to preview things on pages that don't exist lol. if (p->skin < 0)
if (p->mdepth == CSSTEP_CHARS && setup_numplayers == 1) return;
cnum = setup_page;
INT16 skin = setup_chargrid[p->gridx][p->gridy].skinlist[cnum]; if (p->mdepth < CSSTEP_COLORS)
UINT8 color = p->color; color = skins[p->skin].prefcolor;
UINT8 *colormap = R_GetTranslationColormap(skin, color, GTC_MENUCACHE); else
INT32 flags = 0; color = p->color;
colormap = R_GetTranslationColormap(p->skin, color, GTC_MENUCACHE);
// Flip for left-side players M_DrawCharacterSprite(x, y, p->skin, charflip, (p->mdepth == CSSTEP_READY), 0, colormap);
if (!(num & 1))
flags ^= V_FLIP;
if (skin >= 0)
M_DrawCharacterSprite(x, y, skin, flags, colormap);
} }
static void M_DrawCharSelectPreview(UINT8 num) static void M_DrawCharSelectPreview(UINT8 num)
@ -1289,6 +1355,7 @@ static void M_DrawCharSelectPreview(UINT8 num)
INT16 x = 11, y = 5; INT16 x = 11, y = 5;
char letter = 'A' + num; char letter = 'A' + num;
setup_player_t *p = &setup_player[num]; setup_player_t *p = &setup_player[num];
boolean charflip = !!(num & 1);
if (num & 1) if (num & 1)
x += 233; x += 233;
@ -1300,28 +1367,10 @@ static void M_DrawCharSelectPreview(UINT8 num)
if (p->mdepth >= CSSTEP_CHARS) if (p->mdepth >= CSSTEP_CHARS)
{ {
M_DrawCharSelectSprite(num, x+32, y+75); M_DrawCharSelectSprite(num, x+32, y+75, charflip);
if (p->mdepth >= CSSTEP_FOLLOWER)
{
M_DrawFollowerSprite(x+16, y+75, -1, !(num & 1) ? V_FLIP : 0, 0, p);
}
M_DrawCharSelectCircle(p, x+32, y+64); M_DrawCharSelectCircle(p, x+32, y+64);
} }
if ((setup_animcounter/10) & 1 && gamestate == GS_MENU) // Not drawn outside of GS_MENU.
{
if (p->mdepth == CSSTEP_NONE && num == setup_numplayers)
{
V_DrawScaledPatch(x+1, y+36, 0, W_CachePatchName("4PSTART", PU_CACHE));
}
else if (p->mdepth >= CSSTEP_READY)
{
V_DrawScaledPatch(x+1, y+36, 0, W_CachePatchName("4PREADY", PU_CACHE));
}
}
V_DrawScaledPatch(x+9, y+2, 0, W_CachePatchName("FILEBACK", PU_CACHE)); V_DrawScaledPatch(x+9, y+2, 0, W_CachePatchName("FILEBACK", PU_CACHE));
V_DrawScaledPatch(x, y+2, 0, W_CachePatchName(va("CHARSEL%c", letter), PU_CACHE)); V_DrawScaledPatch(x, y+2, 0, W_CachePatchName(va("CHARSEL%c", letter), PU_CACHE));
if (p->mdepth > CSSTEP_PROFILE) if (p->mdepth > CSSTEP_PROFILE)
@ -1334,6 +1383,23 @@ static void M_DrawCharSelectPreview(UINT8 num)
V_DrawFileString(x+16, y+2, 0, "PLAYER"); V_DrawFileString(x+16, y+2, 0, "PLAYER");
} }
if (p->mdepth >= CSSTEP_FOLLOWER)
{
M_DrawFollowerSprite(x+32+((charflip ? 1 : -1)*16), y+75, -1, charflip, 0, 0, p);
}
if ((setup_animcounter/10) & 1 && gamestate == GS_MENU) // Not drawn outside of GS_MENU.
{
if (p->mdepth == CSSTEP_NONE && num == setup_numplayers)
{
V_DrawScaledPatch(x+1, y+36, 0, W_CachePatchName("4PSTART", PU_CACHE));
}
else if (p->mdepth >= CSSTEP_READY)
{
V_DrawScaledPatch(x+1, y+36, 0, W_CachePatchName("4PREADY", PU_CACHE));
}
}
// Profile selection // Profile selection
if (p->mdepth == CSSTEP_PROFILE) if (p->mdepth == CSSTEP_PROFILE)
{ {
@ -1549,39 +1615,38 @@ static void M_DrawProfileCard(INT32 x, INT32 y, boolean greyedout, profile_t *p)
// check what setup_player is doing in priority. // check what setup_player is doing in priority.
if (sp->mdepth >= CSSTEP_CHARS) if (sp->mdepth >= CSSTEP_CHARS)
{ {
skinnum = setup_chargrid[sp->gridx][sp->gridy].skinlist[sp->clonenum]; skinnum = setup_chargrid[sp->gridx][sp->gridy].skinlist[sp->clonenum];
if (skinnum >= 0) if (skinnum >= 0)
{ {
if (M_DrawCharacterSprite(x-22, y+119, skinnum, V_FLIP, colormap)) if (M_DrawCharacterSprite(x-22, y+119, skinnum, false, false, 0, colormap))
V_DrawMappedPatch(x+14, y+66, 0, faceprefix[skinnum][FACE_RANK], colormap); V_DrawMappedPatch(x+14, y+66, 0, faceprefix[skinnum][FACE_RANK], colormap);
if (sp->mdepth >= CSSTEP_FOLLOWER)
{
if (M_DrawFollowerSprite(x-44 +12, y+119, 0, V_FLIP, 0, sp))
{
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);
V_DrawMappedPatch(x+14+18, y+66, 0, ico, fcolormap);
}
}
} }
M_DrawCharSelectCircle(sp, x-22, y+104); M_DrawCharSelectCircle(sp, x-22, y+104);
if (sp->mdepth >= CSSTEP_FOLLOWER)
{
if (M_DrawFollowerSprite(x-22 - 16, y+119, 0, false, 0, 0, sp))
{
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);
V_DrawMappedPatch(x+14+18, y+66, 0, ico, fcolormap);
}
}
} }
else if (skinnum > -1) // otherwise, read from profile. else if (skinnum > -1) // otherwise, read from profile.
{ {
UINT16 col = K_GetEffectiveFollowerColor(p->followercolor, p->color);; UINT16 col = K_GetEffectiveFollowerColor(p->followercolor, p->color);;
UINT8 fln = K_FollowerAvailable(p->follower); UINT8 fln = K_FollowerAvailable(p->follower);
if (M_DrawCharacterSprite(x-22, y+119, skinnum, V_FLIP, colormap)) if (M_DrawCharacterSprite(x-22, y+119, skinnum, false, false, 0, colormap))
V_DrawMappedPatch(x+14, y+66, 0, faceprefix[skinnum][FACE_RANK], colormap); V_DrawMappedPatch(x+14, y+66, 0, faceprefix[skinnum][FACE_RANK], colormap);
if (M_DrawFollowerSprite(x-44 +12, y+119, fln, V_FLIP, col, NULL)) if (M_DrawFollowerSprite(x-22 - 16, y+119, fln, false, 0, col, NULL))
{ {
patch_t *ico = W_CachePatchName(followers[fln].icon, PU_CACHE); patch_t *ico = W_CachePatchName(followers[fln].icon, PU_CACHE);
UINT8 *fcolormap = R_GetTranslationColormap(TC_DEFAULT, col, GTC_MENUCACHE); UINT8 *fcolormap = R_GetTranslationColormap(TC_DEFAULT, col, GTC_MENUCACHE);

View file

@ -1583,6 +1583,9 @@ void M_Ticker(void)
{ {
INT32 i; INT32 i;
if (!menuactive)
return;
if (menutransition.tics != 0 || menutransition.dest != 0) if (menutransition.tics != 0 || menutransition.dest != 0)
{ {
noFurtherInput = true; noFurtherInput = true;
@ -2063,6 +2066,12 @@ static void M_SetupProfileGridPos(setup_player_t *p)
// While we're here, read follower values. // While we're here, read follower values.
p->followern = K_FollowerAvailable(pr->follower); p->followern = K_FollowerAvailable(pr->follower);
if (p->followern < 0 || p->followern >= numfollowers || followers[p->followern].category >= numfollowercategories)
p->followercategory = -1;
else
p->followercategory = followers[p->followern].category;
p->followercolor = pr->followercolor; p->followercolor = pr->followercolor;
// Now position the grid for skin // Now position the grid for skin
@ -2150,6 +2159,7 @@ void M_CharacterSelectInit(void)
{ {
// Default to no follower / match colour. // Default to no follower / match colour.
setup_player[i].followern = -1; setup_player[i].followern = -1;
setup_player[i].followercategory = -1;
setup_player[i].followercolor = FOLLOWERCOLOR_MATCH; setup_player[i].followercolor = FOLLOWERCOLOR_MATCH;
// Set default selected profile to the last used profile for each player: // Set default selected profile to the last used profile for each player:
@ -2486,6 +2496,16 @@ static boolean M_HandleCSelectProfile(setup_player_t *p, UINT8 num)
S_StartSound(NULL, sfx_s3k63); S_StartSound(NULL, sfx_s3k63);
} }
else if (M_MenuExtraPressed(num))
{
UINT8 yourprofile = min(cv_lastprofile[realnum].value, PR_GetNumProfiles());
if (p->profilen == yourprofile)
p->profilen = PROFILE_GUEST;
else
p->profilen = yourprofile;
S_StartSound(NULL, sfx_s3k7b); //sfx_s3kc3s
M_SetMenuDelay(num);
}
return false; return false;
@ -2578,6 +2598,15 @@ static boolean M_HandleCharacterGrid(setup_player_t *p, UINT8 num)
S_StartSound(NULL, sfx_s3k5b); S_StartSound(NULL, sfx_s3k5b);
M_SetMenuDelay(num); M_SetMenuDelay(num);
} }
else if (M_MenuExtraPressed(num))
{
p->gridx /= 3;
p->gridx = (3*p->gridx) + 1;
p->gridy /= 3;
p->gridy = (3*p->gridy) + 1;
S_StartSound(NULL, sfx_s3k7b); //sfx_s3kc3s
M_SetMenuDelay(num);
}
// try to set the clone num to the page # if possible. // try to set the clone num to the page # if possible.
p->clonenum = setup_page; p->clonenum = setup_page;
@ -2692,6 +2721,14 @@ static void M_HandleCharRotate(setup_player_t *p, UINT8 num)
S_StartSound(NULL, sfx_s3k5b); S_StartSound(NULL, sfx_s3k5b);
M_SetMenuDelay(num); M_SetMenuDelay(num);
} }
else if (M_MenuExtraPressed(num))
{
p->clonenum = 0;
p->rotate = CSROTATETICS;
p->hitlag = true;
S_StartSound(NULL, sfx_s3k7b); //sfx_s3kc3s
M_SetMenuDelay(num);
}
} }
static void M_HandleColorRotate(setup_player_t *p, UINT8 num) static void M_HandleColorRotate(setup_player_t *p, UINT8 num)
@ -2716,8 +2753,7 @@ static void M_HandleColorRotate(setup_player_t *p, UINT8 num)
if (M_MenuConfirmPressed(num) /*|| M_MenuButtonPressed(num, MBT_START)*/) if (M_MenuConfirmPressed(num) /*|| M_MenuButtonPressed(num, MBT_START)*/)
{ {
p->mdepth = CSSTEP_FOLLOWER; p->mdepth = CSSTEP_FOLLOWERCATEGORY;
M_GetFollowerState(p);
S_StartSound(NULL, sfx_s3k63); S_StartSound(NULL, sfx_s3k63);
M_SetMenuDelay(num); M_SetMenuDelay(num);
} }
@ -2734,6 +2770,17 @@ static void M_HandleColorRotate(setup_player_t *p, UINT8 num)
S_StartSound(NULL, sfx_s3k5b); S_StartSound(NULL, sfx_s3k5b);
M_SetMenuDelay(num); M_SetMenuDelay(num);
} }
else if (M_MenuExtraPressed(num))
{
if (p->skin >= 0)
{
p->color = skins[p->skin].prefcolor;
p->rotate = CSROTATETICS;
p->hitlag = true;
S_StartSound(NULL, sfx_s3k7b); //sfx_s3kc3s
M_SetMenuDelay(num);
}
}
} }
static void M_AnimateFollower(setup_player_t *p) static void M_AnimateFollower(setup_player_t *p)
@ -2764,16 +2811,103 @@ static void M_AnimateFollower(setup_player_t *p)
p->follower_timer++; p->follower_timer++;
} }
static void M_HandleFollowerRotate(setup_player_t *p, UINT8 num) static void M_HandleFollowerCategoryRotate(setup_player_t *p, UINT8 num)
{ {
if (cv_splitdevice.value) if (cv_splitdevice.value)
num = 0; num = 0;
if (menucmd[num].dpad_lr > 0) if (menucmd[num].dpad_lr > 0)
{ {
p->followern++; p->followercategory++;
if (p->followern >= numfollowers) if (p->followercategory >= numfollowercategories)
p->followercategory = -1;
p->rotate = CSROTATETICS;
p->delay = CSROTATETICS;
S_StartSound(NULL, sfx_s3kc3s);
}
else if (menucmd[num].dpad_lr < 0)
{
p->followercategory--;
if (p->followercategory < -1)
p->followercategory = numfollowercategories-1;
p->rotate = -CSROTATETICS;
p->delay = CSROTATETICS;
S_StartSound(NULL, sfx_s3kc3s);
}
if (M_MenuConfirmPressed(num) /*|| M_MenuButtonPressed(num, MBT_START)*/)
{
if (p->followercategory < 0)
{
p->followern = -1; p->followern = -1;
p->mdepth = CSSTEP_READY;
p->delay = TICRATE;
M_SetupReadyExplosions(p);
S_StartSound(NULL, sfx_s3k4e);
}
else
{
if (p->followern < 0 || followers[p->followern].category != p->followercategory)
{
p->followern = 0;
while (p->followern < numfollowers && followers[p->followern].category != p->followercategory)
p->followern++;
}
if (p->followern >= numfollowers)
{
p->followern = -1;
S_StartSound(NULL, sfx_s3kb2);
}
else
{
M_GetFollowerState(p);
p->mdepth = CSSTEP_FOLLOWER;
S_StartSound(NULL, sfx_s3k63);
}
}
M_SetMenuDelay(num);
}
else if (M_MenuBackPressed(num))
{
p->mdepth = CSSTEP_COLORS;
S_StartSound(NULL, sfx_s3k5b);
M_SetMenuDelay(num);
}
else if (M_MenuExtraPressed(num))
{
if (p->followercategory >= 0 || p->followern < 0 || p->followern >= numfollowers || followers[p->followern].category >= numfollowercategories)
p->followercategory = -1;
else
p->followercategory = followers[p->followern].category;
p->rotate = CSROTATETICS;
p->hitlag = true;
S_StartSound(NULL, sfx_s3k7b); //sfx_s3kc3s
M_SetMenuDelay(num);
}
}
static void M_HandleFollowerRotate(setup_player_t *p, UINT8 num)
{
INT16 startfollowern = p->followern;
if (cv_splitdevice.value)
num = 0;
if (menucmd[num].dpad_lr > 0)
{
do
{
p->followern++;
if (p->followern >= numfollowers)
p->followern = 0;
if (p->followern == startfollowern)
break;
}
while (followers[p->followern].category != p->followercategory);
M_GetFollowerState(p); M_GetFollowerState(p);
@ -2783,9 +2917,17 @@ static void M_HandleFollowerRotate(setup_player_t *p, UINT8 num)
} }
else if (menucmd[num].dpad_lr < 0) else if (menucmd[num].dpad_lr < 0)
{ {
p->followern--; do
if (p->followern < -1) {
p->followern = numfollowers-1; p->followern--;
if (p->followern < 0)
p->followern = numfollowers-1;
if (p->followern == startfollowern)
break;
}
while (followers[p->followern].category != p->followercategory);
M_GetFollowerState(p);
p->rotate = -CSROTATETICS; p->rotate = -CSROTATETICS;
p->delay = CSROTATETICS; p->delay = CSROTATETICS;
@ -2811,10 +2953,19 @@ static void M_HandleFollowerRotate(setup_player_t *p, UINT8 num)
} }
else if (M_MenuBackPressed(num)) else if (M_MenuBackPressed(num))
{ {
p->mdepth = CSSTEP_COLORS; p->mdepth = CSSTEP_FOLLOWERCATEGORY;
S_StartSound(NULL, sfx_s3k5b); S_StartSound(NULL, sfx_s3k5b);
M_SetMenuDelay(num); M_SetMenuDelay(num);
} }
else if (M_MenuExtraPressed(num))
{
p->mdepth = CSSTEP_FOLLOWERCATEGORY;
p->followercategory = -1;
p->rotate = CSROTATETICS;
p->hitlag = true;
S_StartSound(NULL, sfx_s3k7b); //sfx_s3kc3s
M_SetMenuDelay(num);
}
} }
static void M_HandleFollowerColorRotate(setup_player_t *p, UINT8 num) static void M_HandleFollowerColorRotate(setup_player_t *p, UINT8 num)
@ -2849,10 +3000,24 @@ static void M_HandleFollowerColorRotate(setup_player_t *p, UINT8 num)
} }
else if (M_MenuBackPressed(num)) else if (M_MenuBackPressed(num))
{ {
M_GetFollowerState(p);
p->mdepth = CSSTEP_FOLLOWER; p->mdepth = CSSTEP_FOLLOWER;
S_StartSound(NULL, sfx_s3k5b); S_StartSound(NULL, sfx_s3k5b);
M_SetMenuDelay(num); M_SetMenuDelay(num);
} }
else if (M_MenuExtraPressed(num))
{
if (p->followercolor == FOLLOWERCOLOR_MATCH)
p->followercolor = FOLLOWERCOLOR_OPPOSITE;
else if (p->followercolor == followers[p->followern].defaultcolor)
p->followercolor = FOLLOWERCOLOR_MATCH;
else
p->followercolor = followers[p->followern].defaultcolor;
p->rotate = CSROTATETICS;
p->hitlag = true;
S_StartSound(NULL, sfx_s3k7b); //sfx_s3kc3s
M_SetMenuDelay(num);
}
} }
boolean M_CharacterSelectHandler(INT32 choice) boolean M_CharacterSelectHandler(INT32 choice)
@ -2901,6 +3066,9 @@ boolean M_CharacterSelectHandler(INT32 choice)
case CSSTEP_COLORS: // Select color case CSSTEP_COLORS: // Select color
M_HandleColorRotate(p, i); M_HandleColorRotate(p, i);
break; break;
case CSSTEP_FOLLOWERCATEGORY:
M_HandleFollowerCategoryRotate(p, i);
break;
case CSSTEP_FOLLOWER: case CSSTEP_FOLLOWER:
M_HandleFollowerRotate(p, i); M_HandleFollowerRotate(p, i);
break; break;
@ -2956,33 +3124,27 @@ 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];
// colour // colour
// (convert the number that's saved to a string we can use) // (convert the number that's saved to a string we can use)
col = setup_player[i].color; col = setup_player[i].color;
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");
// follower color else
CV_StealthSetValue(&cv_followercolor[i], setup_player[i].followercolor); CV_StealthSet(&cv_follower[i], followers[setup_player[i].followern].name);
// finally, call the skin[x] console command. // finally, call the skin[x] console command.
// This will call SendNameAndColor which will synch everything we sent here and apply the changes! // This will call SendNameAndColor which will synch everything we sent here and apply the changes!
// This is a hack to make sure we call Skin[x]_OnChange afterwards CV_StealthSet(&cv_skin[i], skins[setup_player[i].skin].name);
CV_StealthSetValue(&cv_skin[i], -1);
strcpy(cmd, commandnames[i]); // ...actually, let's do this last - Skin_OnChange has some return-early occasions
strcat(cmd, skins[setup_player[i].skin].name); // follower color
COM_ImmedExecute(cmd); CV_SetValue(&cv_followercolor[i], setup_player[i].followercolor);
} }
M_ClearMenus(true); M_ClearMenus(true);
@ -3004,6 +3166,8 @@ void M_CharacterSelectTick(void)
setup_player[i].rotate--; setup_player[i].rotate--;
else if (setup_player[i].rotate < 0) else if (setup_player[i].rotate < 0)
setup_player[i].rotate++; setup_player[i].rotate++;
else
setup_player[i].hitlag = false;
if (i >= setup_numplayers) if (i >= setup_numplayers)
continue; continue;
@ -3034,7 +3198,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
@ -3051,7 +3215,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);
} }