diff --git a/src/d_clisrv.c b/src/d_clisrv.c index f8eaf2bca..b71ab15a5 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -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); diff --git a/src/d_netcmd.c b/src/d_netcmd.c index f19c0f2fc..1f09000f4 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -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")); diff --git a/src/deh_soc.c b/src/deh_soc.c index 4ae9927f4..1455f0641 100644 --- a/src/deh_soc.c +++ b/src/deh_soc.c @@ -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); diff --git a/src/deh_soc.h b/src/deh_soc.h index 161422783..0d9beccab 100644 --- a/src/deh_soc.h +++ b/src/deh_soc.h @@ -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); diff --git a/src/dehacked.c b/src/dehacked.c index 690a49dbc..99ee7b0e8 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -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) { diff --git a/src/g_demo.c b/src/g_demo.c index f602e2752..7978d0ef6 100644 --- a/src/g_demo.c +++ b/src/g_demo.c @@ -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 diff --git a/src/g_game.c b/src/g_game.c index 8db8fac2c..db838ab91 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -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 diff --git a/src/k_follower.c b/src/k_follower.c index 63ebda167..636620ec8 100644 --- a/src/k_follower.c +++ b/src/k_follower.c @@ -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; } diff --git a/src/k_follower.h b/src/k_follower.h index f9a16c971..3c1e2a7d1 100644 --- a/src/k_follower.h +++ b/src/k_follower.h @@ -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__ diff --git a/src/k_menu.h b/src/k_menu.h index ba4458bcc..c64ff4d2d 100644 --- a/src/k_menu.h +++ b/src/k_menu.h @@ -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; diff --git a/src/k_menudraw.c b/src/k_menudraw.c index 84393be4b..d757ea799 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -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<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<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<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<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); diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 4babf145b..4d5d401af 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -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); }