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;
if (players[playernum].follower)
{
K_RemoveFollower(&players[playernum]);
}
if (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...
consvar_t cv_followercolor[MAXSPLITSCREENPLAYERS] = {
CVAR_INIT ("followercolor", "1", 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 ("followercolor3", "1", 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 ("followercolor", "Match", CV_SAVE|CV_CALL|CV_NOINIT, Followercolor_cons_t, Followercolor_OnChange),
CVAR_INIT ("followercolor2", "Match", CV_SAVE|CV_CALL|CV_NOINIT, Followercolor_cons_t, Followercolor2_OnChange),
CVAR_INIT ("followercolor3", "Match", CV_SAVE|CV_CALL|CV_NOINIT, Followercolor_cons_t, Followercolor3_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.
@ -831,27 +831,6 @@ void D_RegisterClientCommands(void)
{
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
// Monster Iestyn (12/08/19): not sure where else I could have actually put this, but oh well
for (i = 0; i < MAXPLAYERS; i++)
@ -1493,7 +1472,7 @@ static void SendNameAndColor(UINT8 n)
const INT32 playernum = g_localplayers[n];
player_t *player = &players[playernum];
char buf[MAXPLAYERNAME+9];
char buf[MAXPLAYERNAME+12];
char *p;
if (splitscreen < n)
@ -1524,14 +1503,11 @@ static void SendNameAndColor(UINT8 n)
if (!cv_followercolor[n].value)
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])
&& cv_playercolor[n].value == player->skincolor
&& !strcmp(cv_skin[n].string, skins[player->skin].name)
&& cv_follower[n].value == player->followerskin
&& !stricmp(cv_skin[n].string, skins[player->skin].name)
&& !stricmp(cv_follower[n].string,
(player->followerskin < 0 ? "None" : followers[player->followerskin].name))
&& cv_followercolor[n].value == player->followercolor)
return;
@ -1553,31 +1529,28 @@ static void SendNameAndColor(UINT8 n)
K_KartResetPlayerColor(player);
// Update follower for local games:
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))
if ((foundskin = R_SkinAvailable(cv_skin[n].string)) != -1 && R_SkinUsable(playernum, foundskin))
{
cv_skin[n].value = foundskin;
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
{
cv_skin[n].value = players[playernum].skin;
CV_StealthSet(&cv_skin[n], skins[player->skin].name);
cv_skin[n].value = player->skin;
// will always be same as current
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;
}
@ -1607,12 +1580,20 @@ static void SendNameAndColor(UINT8 n)
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.
WRITESTRINGN(p, cv_playername[n].zstring, MAXPLAYERNAME);
WRITEUINT32(p, (UINT32)player->availabilities);
WRITEUINT16(p, (UINT16)cv_playercolor[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);
SendNetXCmdForPlayer(n, XD_NAMEANDCOLOR, buf, p - buf);
@ -1624,7 +1605,7 @@ static void Got_NameAndColor(UINT8 **cp, INT32 playernum)
char name[MAXPLAYERNAME+1];
UINT16 color, followercolor;
UINT8 skin;
SINT8 follower;
INT16 follower;
SINT8 localplayer = -1;
UINT8 i;
@ -1653,7 +1634,8 @@ static void Got_NameAndColor(UINT8 **cp, INT32 playernum)
p->availabilities = READUINT32(*cp);
color = READUINT16(*cp);
skin = READUINT8(*cp);
follower = READSINT8(*cp);
follower = READINT16(*cp);
//CONS_Printf("Recieved follower id %d\n", follower);
followercolor = READUINT16(*cp);
// 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"));
CV_StealthSet(&cv_playername[0], player_names[consoleplayer]);
return;
}
else
SendNameAndColor(0);
@ -6119,207 +6102,56 @@ static void Name4_OnChange(void)
}
// 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())
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.
static void Followercolor_OnChange(void)
{
if (!Playing())
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);
}
FollowerAny_OnChange(0);
}
// repeat for the 3 other players
static void Follower2_OnChange(void)
{
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[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);
FollowerAny_OnChange(1);
}
static void Followercolor2_OnChange(void)
{
if (!Playing())
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);
}
FollowerAny_OnChange(1);
}
static void Follower3_OnChange(void)
{
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[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);
FollowerAny_OnChange(2);
}
static void Followercolor3_OnChange(void)
{
if (!Playing())
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);
}
FollowerAny_OnChange(2);
}
static void Follower4_OnChange(void)
{
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[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);
FollowerAny_OnChange(3);
}
static void Followercolor4_OnChange(void)
{
if (!Playing())
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);
}
FollowerAny_OnChange(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
@ -6339,7 +6171,9 @@ static void Skin_OnChange(void)
}
if (CanChangeSkinWhilePlaying(consoleplayer))
{
SendNameAndColor(0);
}
else
{
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)
{
char *s;
char *word, *word2, dname[SKINNAMESIZE+1];
char *word, *word2;
char *tmp;
char testname[SKINNAMESIZE+1];
@ -3139,10 +3139,9 @@ void readfollower(MYFILE *f)
INT32 res;
INT32 i;
if (numfollowers > MAXSKINS)
if (numfollowers >= MAXSKINS)
{
deh_warning("Error: Too many followers, cannot add anymore.\n");
return;
I_Error("Out of Followers\nLoad less addons to fix this.");
}
s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL);
@ -3161,8 +3160,9 @@ void readfollower(MYFILE *f)
followers[numfollowers].bobspeed = TICRATE*2;
followers[numfollowers].bobamp = 4*FRACUNIT;
followers[numfollowers].hitconfirmtime = TICRATE;
followers[numfollowers].defaultcolor = SKINCOLOR_GREEN;
strcpy(followers[numfollowers].icon, "M_NORANK");
followers[numfollowers].defaultcolor = FOLLOWERCOLOR_MATCH;
followers[numfollowers].category = UINT8_MAX;
strcpy(followers[numfollowers].icon, "MISSING");
do
{
@ -3201,6 +3201,23 @@ void readfollower(MYFILE *f)
strcpy(followers[numfollowers].icon, word2);
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"))
{
if (word2)
@ -3215,7 +3232,20 @@ void readfollower(MYFILE *f)
}
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"))
{
@ -3318,10 +3348,6 @@ void readfollower(MYFILE *f)
// 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...
strcpy(testname, followers[numfollowers].name);
// lower testname for skin checks...
strlwr(testname);
res = K_FollowerAvailable(testname);
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.
}
strcpy(followers[numfollowers].skinname, testname);
strcpy(dname, followers[numfollowers].skinname); // display name, just used for printing succesful stuff or errors later down the line.
strcpy(testname, followers[numfollowers].name);
// 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++)
@ -3349,14 +3374,14 @@ void readfollower(MYFILE *f)
if (followers[numfollowers].mode < FOLLOWERMODE_FLOAT || followers[numfollowers].mode >= FOLLOWERMODE__MAX)
{
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) \
if ((signed)followers[numfollowers].field < threshold) \
{ \
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);
@ -3373,13 +3398,6 @@ if ((signed)followers[numfollowers].field < threshold) \
#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.
// 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; \
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");
@ -3399,11 +3417,83 @@ if (!followers[numfollowers].field) \
NOSTATE(hitconfirmstate, "HITCONFIRMSTATE");
#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
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)
{
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 readfollower(MYFILE *f);
void readfollowercategory(MYFILE *f);
preciptype_t get_precip(const char *word);
void readweather(MYFILE *f, INT32 num);

View file

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

View file

@ -313,11 +313,11 @@ void G_ReadDemoExtraData(void)
demo_p += 16;
for (i = 0; i < numskincolors +2; i++) // +2 because of Match and Opposite
{
if (!stricmp(Followercolor_cons_t[i].strvalue, name))
{
players[p].followercolor = i;
break;
}
if (!stricmp(Followercolor_cons_t[i].strvalue, name))
{
players[p].followercolor = Followercolor_cons_t[i].value;
break;
}
}
}
if (extradata & DXD_PLAYSTATE)
@ -407,7 +407,7 @@ void G_ReadDemoExtraData(void)
void G_WriteDemoExtraData(void)
{
INT32 i;
INT32 i, j;
char name[16];
for (i = 0; i < MAXPLAYERS; i++)
@ -453,13 +453,18 @@ void G_WriteDemoExtraData(void)
if (players[i].followerskin == -1)
strncpy(name, "None", 16);
else
strncpy(name, followers[players[i].followerskin].skinname, 16);
strncpy(name, followers[players[i].followerskin].name, 16);
M_Memcpy(demo_p, name, 16);
demo_p += 16;
// write follower color
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);
demo_p += 16;
@ -1951,7 +1956,7 @@ void G_RecordMetal(void)
void G_BeginRecording(void)
{
UINT8 i, p;
UINT8 i, j, p;
char name[MAXCOLORNAME+1];
player_t *player = &players[consoleplayer];
@ -2088,7 +2093,7 @@ void G_BeginRecording(void)
memset(name, 0, 16);
if (player->follower)
strncpy(name, followers[player->followerskin].skinname, 16);
strncpy(name, followers[player->followerskin].name, 16);
else
strncpy(name, "None", 16); // Say we don't have one, then.
@ -2097,7 +2102,12 @@ void G_BeginRecording(void)
// Save follower's colour
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);
demo_p += 16;
@ -3073,11 +3083,11 @@ void G_DoPlayDemo(char *defdemoname)
demo_p += 16;
for (i = 0; i < numskincolors +2; i++) // +2 because of Match and Opposite
{
if (!stricmp(Followercolor_cons_t[i].strvalue, color))
{
players[p].followercolor = i;
break;
}
if (!stricmp(Followercolor_cons_t[i].strvalue, color))
{
players[p].followercolor = Followercolor_cons_t[i].value;
break;
}
}
// 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.
void G_LoadGameSettings(void)
{
INT32 i;
// initialize free sfx slots for skin sounds
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

View file

@ -15,6 +15,9 @@
INT32 numfollowers = 0;
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
/*--------------------------------------------------
@ -28,7 +31,7 @@ INT32 K_FollowerAvailable(const char *name)
for (i = 0; i < numfollowers; i++)
{
if (stricmp(followers[i].skinname, name) == 0)
if (stricmp(followers[i].name, name) == 0)
return i;
}
@ -54,7 +57,7 @@ boolean K_SetFollowerByName(INT32 playernum, const char *skinname)
for (i = 0; i < numfollowers; i++)
{
// search in the skin list
if (stricmp(followers[i].skinname, skinname) == 0)
if (stricmp(followers[i].name, skinname) == 0)
{
K_SetFollowerByNum(playernum, i);
return true;
@ -74,6 +77,31 @@ boolean K_SetFollowerByName(INT32 playernum, const char *skinname)
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)
@ -82,8 +110,6 @@ boolean K_SetFollowerByName(INT32 playernum, const char *skinname)
void K_SetFollowerByNum(INT32 playernum, INT32 skinnum)
{
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.
@ -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.
*/
if (player->follower && skinnum != player->followerskin) // 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);
}
if (skinnum != player->followerskin)
K_RemoveFollower(player);
player->followerskin = skinnum;
@ -253,18 +266,16 @@ void K_HandleFollower(player_t *player)
{
//CONS_Printf("Follower skin invlaid. Setting to -1.\n");
player->followerskin = -1;
return;
}
// don't do anything if we can't have a follower to begin with.
// (It gets removed under those conditions)
if (player->spectator)
{
return;
}
if (player->followerskin < 0)
if (player->spectator || player->followerskin < 0)
{
if (player->follower)
{
K_RemoveFollower(player);
}
return;
}

View file

@ -45,10 +45,11 @@ typedef enum
//
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]; // Name. This is used for the menus. We'll just follow the same rules as skins for this.
char name[SKINNAMESIZE+1]; // Skin Name. This is what to refer to when asking the commands anything..
char icon[8+1]; // Lump names are only 8 characters. (+1 for \0)
UINT8 category; // Category
skincolornum_t defaultcolor; // default color for menus.
followermode_t mode; // Follower behavior modifier.
@ -85,6 +86,18 @@ typedef struct follower_s
extern INT32 numfollowers;
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)
@ -168,5 +181,19 @@ UINT16 K_GetEffectiveFollowerColor(UINT16 followercolor, UINT16 playercolor);
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__

View file

@ -599,6 +599,7 @@ typedef enum
CSSTEP_CHARS,
CSSTEP_ALTS,
CSSTEP_COLORS,
CSSTEP_FOLLOWERCATEGORY,
CSSTEP_FOLLOWER,
CSSTEP_FOLLOWERCOLORS,
CSSTEP_READY
@ -614,6 +615,7 @@ typedef struct setup_player_s
UINT8 delay;
UINT16 color;
UINT8 mdepth;
boolean hitlag;
// 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.
@ -622,7 +624,8 @@ typedef struct setup_player_s
UINT8 changeselect;
INT32 followern;
INT16 followercategory;
INT16 followern;
UINT16 followercolor;
tic_t follower_tics;
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;
UINT16 i, numoptions = 0;
UINT16 l = 0, r = 0;
INT16 l = 0, r = 0;
INT16 subtractcheck;
switch (p->mdepth)
{
@ -979,8 +980,11 @@ static void M_DrawCharSelectCircle(setup_player_t *p, INT16 x, INT16 y)
case CSSTEP_COLORS:
numoptions = nummenucolors;
break;
case CSSTEP_FOLLOWERCATEGORY:
numoptions = numfollowercategories+1;
break;
case CSSTEP_FOLLOWER:
numoptions = numfollowers+1;
numoptions = followercategories[p->followercategory].numincategory;
break;
case CSSTEP_FOLLOWERCOLORS:
numoptions = nummenucolors+2;
@ -994,17 +998,19 @@ static void M_DrawCharSelectCircle(setup_player_t *p, INT16 x, INT16 y)
return;
}
subtractcheck = 1 ^ (numoptions & 1);
angamt /= numoptions;
for (i = 0; i < numoptions; i++)
{
fixed_t cx = x << FRACBITS, cy = y << FRACBITS;
boolean subtract = (i & 1);
boolean subtract = (i & 1) == subtractcheck;
angle_t ang = ((i+1)/2) * angamt;
patch_t *patch = NULL;
UINT8 *colormap = NULL;
fixed_t radius = 28<<FRACBITS;
UINT16 n = 0;
INT16 n = 0;
switch (p->mdepth)
{
@ -1017,7 +1023,7 @@ static void M_DrawCharSelectCircle(setup_player_t *p, INT16 x, INT16 y)
n -= ((i+1)/2);
else
n += ((i+1)/2);
n %= numoptions;
n = (n + numoptions) % numoptions;
skin = setup_chargrid[p->gridx][p->gridy].skinlist[n];
patch = faceprefix[skin][FACE_RANK];
@ -1061,16 +1067,16 @@ static void M_DrawCharSelectCircle(setup_player_t *p, INT16 x, INT16 y)
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)
n -= ((i+1)/2);
else
n += ((i+1)/2);
n %= numoptions;
n = (n + numoptions) % numoptions;
if (n == 0)
{
@ -1078,11 +1084,71 @@ static void M_DrawCharSelectCircle(setup_player_t *p, INT16 x, INT16 y)
}
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);
colormap = R_GetTranslationColormap(TC_DEFAULT,
K_GetEffectiveFollowerColor(p->followercolor, p->color),
K_GetEffectiveFollowerColor(fl->defaultcolor, p->color),
GTC_MENUCACHE
);
}
@ -1142,7 +1208,12 @@ static void M_DrawCharSelectCircle(setup_player_t *p, INT16 x, INT16 y)
ang = (signed)(ang - (angamt/2));
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));
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
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;
spritedef_t *sprdef;
spriteframe_t *sprframe;
patch_t *sprpatch;
UINT8 rotation = (charflip ? 1 : 7);
UINT32 frame = animate ? setup_animcounter : 0;
UINT32 flags = 0;
UINT32 frame;
spr = P_GetSkinSprite2(&skins[skin], SPR2_FSTN, NULL);
spr = P_GetSkinSprite2(&skins[skin], SPR2_STIN, NULL);
sprdef = &skins[skin].sprites[spr];
if (!sprdef->numframes) // No frames ??
return false; // Can't render!
frame = states[S_KART_FAST].frame & FF_FRAMEMASK;
if (frame >= sprdef->numframes) // Walking animation missing
frame = 0; // Try to use standing frame
frame %= sprdef->numframes;
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
flags |= V_FLIP; // This sprite is left/right flipped!
if (sprframe->flip & (1<<rotation)) // Only for first sprite
{
addflags ^= V_FLIP; // This sprite is left/right flipped!
}
if (skins[skin].flags & SF_HIRES)
{
V_DrawFixedPatch(x<<FRACBITS,
y<<FRACBITS,
skins[skin].highresscale,
flags, sprpatch, colormap);
addflags, sprpatch, colormap);
}
else
V_DrawMappedPatch(x, y, addflags|flags, sprpatch, colormap);
V_DrawMappedPatch(x, y, addflags, sprpatch, colormap);
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.
// '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, 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;
@ -1207,6 +1277,7 @@ static boolean M_DrawFollowerSprite(INT16 x, INT16 y, INT32 num, INT32 addflags,
UINT32 useframe;
follower_t fl;
UINT8 *colormap = NULL;
UINT8 rotation = (charflip ? 1 : 7);
if (p != NULL)
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?
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;
else
addflags |= V_FLIP; // This sprite is left/right flipped!
addflags ^= V_FLIP; // This sprite is left/right flipped!
}
fixed_t sine = 0;
@ -1253,35 +1321,33 @@ static boolean M_DrawFollowerSprite(INT16 x, INT16 y, INT32 num, INT32 addflags,
if (p != NULL)
{
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);
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;
}
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];
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->mdepth == CSSTEP_CHARS && setup_numplayers == 1)
cnum = setup_page;
if (p->skin < 0)
return;
INT16 skin = setup_chargrid[p->gridx][p->gridy].skinlist[cnum];
UINT8 color = p->color;
UINT8 *colormap = R_GetTranslationColormap(skin, color, GTC_MENUCACHE);
INT32 flags = 0;
if (p->mdepth < CSSTEP_COLORS)
color = skins[p->skin].prefcolor;
else
color = p->color;
colormap = R_GetTranslationColormap(p->skin, color, GTC_MENUCACHE);
// Flip for left-side players
if (!(num & 1))
flags ^= V_FLIP;
if (skin >= 0)
M_DrawCharacterSprite(x, y, skin, flags, colormap);
M_DrawCharacterSprite(x, y, p->skin, charflip, (p->mdepth == CSSTEP_READY), 0, colormap);
}
static void M_DrawCharSelectPreview(UINT8 num)
@ -1289,6 +1355,7 @@ static void M_DrawCharSelectPreview(UINT8 num)
INT16 x = 11, y = 5;
char letter = 'A' + num;
setup_player_t *p = &setup_player[num];
boolean charflip = !!(num & 1);
if (num & 1)
x += 233;
@ -1300,28 +1367,10 @@ static void M_DrawCharSelectPreview(UINT8 num)
if (p->mdepth >= CSSTEP_CHARS)
{
M_DrawCharSelectSprite(num, x+32, y+75);
if (p->mdepth >= CSSTEP_FOLLOWER)
{
M_DrawFollowerSprite(x+16, y+75, -1, !(num & 1) ? V_FLIP : 0, 0, p);
}
M_DrawCharSelectSprite(num, x+32, y+75, charflip);
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, y+2, 0, W_CachePatchName(va("CHARSEL%c", letter), PU_CACHE));
if (p->mdepth > CSSTEP_PROFILE)
@ -1334,6 +1383,23 @@ static void M_DrawCharSelectPreview(UINT8 num)
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
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.
if (sp->mdepth >= CSSTEP_CHARS)
{
skinnum = setup_chargrid[sp->gridx][sp->gridy].skinlist[sp->clonenum];
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);
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);
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.
{
UINT16 col = K_GetEffectiveFollowerColor(p->followercolor, p->color);;
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);
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);
UINT8 *fcolormap = R_GetTranslationColormap(TC_DEFAULT, col, GTC_MENUCACHE);

View file

@ -1583,6 +1583,9 @@ void M_Ticker(void)
{
INT32 i;
if (!menuactive)
return;
if (menutransition.tics != 0 || menutransition.dest != 0)
{
noFurtherInput = true;
@ -2063,6 +2066,12 @@ static void M_SetupProfileGridPos(setup_player_t *p)
// 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;
else
p->followercategory = followers[p->followern].category;
p->followercolor = pr->followercolor;
// Now position the grid for skin
@ -2150,6 +2159,7 @@ void M_CharacterSelectInit(void)
{
// Default to no follower / match colour.
setup_player[i].followern = -1;
setup_player[i].followercategory = -1;
setup_player[i].followercolor = FOLLOWERCOLOR_MATCH;
// 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);
}
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;
@ -2578,6 +2598,15 @@ static boolean M_HandleCharacterGrid(setup_player_t *p, UINT8 num)
S_StartSound(NULL, sfx_s3k5b);
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.
p->clonenum = setup_page;
@ -2692,6 +2721,14 @@ static void M_HandleCharRotate(setup_player_t *p, UINT8 num)
S_StartSound(NULL, sfx_s3k5b);
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)
@ -2716,8 +2753,7 @@ static void M_HandleColorRotate(setup_player_t *p, UINT8 num)
if (M_MenuConfirmPressed(num) /*|| M_MenuButtonPressed(num, MBT_START)*/)
{
p->mdepth = CSSTEP_FOLLOWER;
M_GetFollowerState(p);
p->mdepth = CSSTEP_FOLLOWERCATEGORY;
S_StartSound(NULL, sfx_s3k63);
M_SetMenuDelay(num);
}
@ -2734,6 +2770,17 @@ static void M_HandleColorRotate(setup_player_t *p, UINT8 num)
S_StartSound(NULL, sfx_s3k5b);
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)
@ -2764,16 +2811,103 @@ static void M_AnimateFollower(setup_player_t *p)
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)
num = 0;
if (menucmd[num].dpad_lr > 0)
{
p->followern++;
if (p->followern >= numfollowers)
p->followercategory++;
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->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);
@ -2783,9 +2917,17 @@ static void M_HandleFollowerRotate(setup_player_t *p, UINT8 num)
}
else if (menucmd[num].dpad_lr < 0)
{
p->followern--;
if (p->followern < -1)
p->followern = numfollowers-1;
do
{
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->delay = CSROTATETICS;
@ -2811,10 +2953,19 @@ static void M_HandleFollowerRotate(setup_player_t *p, UINT8 num)
}
else if (M_MenuBackPressed(num))
{
p->mdepth = CSSTEP_COLORS;
p->mdepth = CSSTEP_FOLLOWERCATEGORY;
S_StartSound(NULL, sfx_s3k5b);
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)
@ -2849,10 +3000,24 @@ static void M_HandleFollowerColorRotate(setup_player_t *p, UINT8 num)
}
else if (M_MenuBackPressed(num))
{
M_GetFollowerState(p);
p->mdepth = CSSTEP_FOLLOWER;
S_StartSound(NULL, sfx_s3k5b);
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)
@ -2901,6 +3066,9 @@ boolean M_CharacterSelectHandler(INT32 choice)
case CSSTEP_COLORS: // Select color
M_HandleColorRotate(p, i);
break;
case CSSTEP_FOLLOWERCATEGORY:
M_HandleFollowerCategoryRotate(p, i);
break;
case CSSTEP_FOLLOWER:
M_HandleFollowerRotate(p, i);
break;
@ -2956,33 +3124,27 @@ static void M_MPConfirmCharacterSelection(void)
UINT8 i;
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++)
{
char cmd[MAXSTRINGLENGTH];
// colour
// (convert the number that's saved to a string we can use)
col = setup_player[i].color;
CV_StealthSetValue(&cv_playercolor[i], col);
// follower
CV_StealthSetValue(&cv_follower[i], setup_player[i].followern);
// follower color
CV_StealthSetValue(&cv_followercolor[i], setup_player[i].followercolor);
if (setup_player[i].followern < 0)
CV_StealthSet(&cv_follower[i], "None");
else
CV_StealthSet(&cv_follower[i], followers[setup_player[i].followern].name);
// finally, call the skin[x] console command.
// 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_StealthSetValue(&cv_skin[i], -1);
CV_StealthSet(&cv_skin[i], skins[setup_player[i].skin].name);
strcpy(cmd, commandnames[i]);
strcat(cmd, skins[setup_player[i].skin].name);
COM_ImmedExecute(cmd);
// ...actually, let's do this last - Skin_OnChange has some return-early occasions
// follower color
CV_SetValue(&cv_followercolor[i], setup_player[i].followercolor);
}
M_ClearMenus(true);
@ -3004,6 +3166,8 @@ void M_CharacterSelectTick(void)
setup_player[i].rotate--;
else if (setup_player[i].rotate < 0)
setup_player[i].rotate++;
else
setup_player[i].hitlag = false;
if (i >= setup_numplayers)
continue;
@ -3034,7 +3198,7 @@ void M_CharacterSelectTick(void)
optionsmenu.profile->color = setup_player[0].color;
// 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;
// reset setup_player
@ -3051,7 +3215,10 @@ void M_CharacterSelectTick(void)
CV_StealthSet(&cv_skin[i], skins[setup_player[i].skin].name);
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);
}