From f5eadd331e65c473b021772dda8a074d01996f4c Mon Sep 17 00:00:00 2001 From: toaster Date: Sun, 6 Nov 2022 14:01:00 +0000 Subject: [PATCH 01/12] Fix follower colour handling in replays --- src/g_demo.c | 38 ++++++++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/src/g_demo.c b/src/g_demo.c index 0fbd920c6..551477d2e 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++) @@ -459,7 +459,12 @@ void G_WriteDemoExtraData(void) // 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]; @@ -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; @@ -3070,11 +3080,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 From ca3525a81a65f1de8fb5dc5d88d4f8753756e97e Mon Sep 17 00:00:00 2001 From: toaster Date: Sun, 6 Nov 2022 14:52:37 +0000 Subject: [PATCH 02/12] Fixing going back in the follower list on the Character Select --- src/k_menufunc.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 4babf145b..8cd8ef7cc 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -2787,6 +2787,8 @@ static void M_HandleFollowerRotate(setup_player_t *p, UINT8 num) if (p->followern < -1) p->followern = numfollowers-1; + M_GetFollowerState(p); + p->rotate = -CSROTATETICS; p->delay = CSROTATETICS; S_StartSound(NULL, sfx_s3kc3s); From 1cae2e1502551e0f24df116a83b91b4ea3093263 Mon Sep 17 00:00:00 2001 From: toaster Date: Sun, 6 Nov 2022 15:21:22 +0000 Subject: [PATCH 03/12] Fix loading follower default colour Also corrects an issue where the last two skincolours were not being copied to the followercolor table --- src/d_netcmd.c | 21 --------------------- src/deh_soc.c | 24 +++++++++++++++--------- src/g_game.c | 18 ++++++++++++++++++ 3 files changed, 33 insertions(+), 30 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 51d205931..b62840e45 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -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++) diff --git a/src/deh_soc.c b/src/deh_soc.c index 4ae9927f4..c19db90ae 100644 --- a/src/deh_soc.c +++ b/src/deh_soc.c @@ -3161,7 +3161,7 @@ void readfollower(MYFILE *f) followers[numfollowers].bobspeed = TICRATE*2; followers[numfollowers].bobamp = 4*FRACUNIT; followers[numfollowers].hitconfirmtime = TICRATE; - followers[numfollowers].defaultcolor = SKINCOLOR_GREEN; + followers[numfollowers].defaultcolor = FOLLOWERCOLOR_MATCH; strcpy(followers[numfollowers].icon, "M_NORANK"); do @@ -3215,7 +3215,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")) { @@ -3373,13 +3386,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. 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 From 44f869e5e65d4945ab6611618bf487d6a5dff36b Mon Sep 17 00:00:00 2001 From: toaster Date: Sun, 6 Nov 2022 16:53:57 +0000 Subject: [PATCH 04/12] Adjust followers (and players) drawing on character select - Use default colours for followers when not select(ing/ed) follower colour, to match character selection - Adjust offsets in general, including a rework bobbing handling, so there isn't a huge variance in follower position on-screen - Move followers to draw in front of several elements, including the "A/B/C/D PLAYER" text and the rotatey wheel so you can actually see them - Also applies to player: Don't mirror between sides of the screen - instead, rotate the virtual player object, so asymmetrical details aren't lost! - Player-specific: Animate when READY !! to simlate starting your engine. --- src/k_menudraw.c | 134 +++++++++++++++++++++++------------------------ 1 file changed, 65 insertions(+), 69 deletions(-) diff --git a/src/k_menudraw.c b/src/k_menudraw.c index 84393be4b..9ecb857e0 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -1082,7 +1082,7 @@ static void M_DrawCharSelectCircle(setup_player_t *p, INT16 x, INT16 y) 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 ); } @@ -1154,41 +1154,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 +1238,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; + INT16 skin; + 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; - 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; - - // Flip for left-side players - if (!(num & 1)) - flags ^= V_FLIP; + skin = setup_chargrid[p->gridx][p->gridy].skinlist[cnum]; + color = p->color; + colormap = R_GetTranslationColormap(skin, color, GTC_MENUCACHE); if (skin >= 0) - M_DrawCharacterSprite(x, y, skin, flags, colormap); + M_DrawCharacterSprite(x, y, skin, charflip, (p->mdepth == CSSTEP_READY), 0, colormap); } static void M_DrawCharSelectPreview(UINT8 num) @@ -1289,6 +1286,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 +1298,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 +1314,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 +1546,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); From 59cd7bfc11fd1ca914e2f66ee82c13d3e2209a68 Mon Sep 17 00:00:00 2001 From: toaster Date: Sun, 6 Nov 2022 17:06:29 +0000 Subject: [PATCH 05/12] Use character's prefcolour before color selection for that player --- src/k_menudraw.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/k_menudraw.c b/src/k_menudraw.c index 9ecb857e0..a7baafe14 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -1274,7 +1274,10 @@ static void M_DrawCharSelectSprite(UINT8 num, INT16 x, INT16 y, boolean charflip cnum = setup_page; skin = setup_chargrid[p->gridx][p->gridy].skinlist[cnum]; - color = p->color; + if (p->mdepth < CSSTEP_COLORS) + color = skins[skin].prefcolor; + else + color = p->color; colormap = R_GetTranslationColormap(skin, color, GTC_MENUCACHE); if (skin >= 0) From 368a45c6742ac765aa0c0fde1b76aa49dd19dbf8 Mon Sep 17 00:00:00 2001 From: toaster Date: Sun, 6 Nov 2022 22:15:08 +0000 Subject: [PATCH 06/12] Change default followercolor to "Match" (from "1") --- src/d_netcmd.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index b62840e45..1c527b1a5 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. From 8aeaf9738d3a013352e16c4cb8d4537c694f037c Mon Sep 17 00:00:00 2001 From: toaster Date: Sun, 6 Nov 2022 22:53:12 +0000 Subject: [PATCH 07/12] Major changes to follower selection - Followers now have categories, definable in SOC - New character select step: Follower category - "None" is a category, just skips straight to Follower None - Select a category to go to the previous regular follower selection step - Press the C/Extra button to reset a character select step to its "default" - Character: Center the character in the engine class (goes from [0,1] to [1,1], etc) - Character alts: Centers the "primary" alt (prefers Eggman over Eggrobo) - Skincolor: Centers the character's prefcolour - Follower category: Centers on the "None" option - Follower: [CURRENTLY NO BEHAVIOUR] - Followercolor: Cycles between follower's defaultcolour, "Match", and "Opposite" --- src/deh_soc.c | 97 +++++++++++++++++++++++++++-- src/deh_soc.h | 1 + src/dehacked.c | 6 ++ src/k_follower.c | 3 + src/k_follower.h | 14 +++++ src/k_menu.h | 5 +- src/k_menudraw.c | 108 ++++++++++++++++++++++++++------- src/k_menufunc.c | 155 ++++++++++++++++++++++++++++++++++++++++++++--- 8 files changed, 354 insertions(+), 35 deletions(-) diff --git a/src/deh_soc.c b/src/deh_soc.c index c19db90ae..1d806322e 100644 --- a/src/deh_soc.c +++ b/src/deh_soc.c @@ -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); @@ -3162,7 +3161,8 @@ void readfollower(MYFILE *f) followers[numfollowers].bobamp = 4*FRACUNIT; followers[numfollowers].hitconfirmtime = TICRATE; followers[numfollowers].defaultcolor = FOLLOWERCOLOR_MATCH; - strcpy(followers[numfollowers].icon, "M_NORANK"); + 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) @@ -3406,10 +3423,82 @@ if (!followers[numfollowers].field) \ #undef NOSTATE CONS_Printf("Added follower '%s'\n", dname); + 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/k_follower.c b/src/k_follower.c index 63ebda167..c6a9e97eb 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 /*-------------------------------------------------- diff --git a/src/k_follower.h b/src/k_follower.h index f9a16c971..4bf39dbc0 100644 --- a/src/k_follower.h +++ b/src/k_follower.h @@ -49,6 +49,8 @@ typedef struct follower_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 category; // Category + skincolornum_t defaultcolor; // default color for menus. followermode_t mode; // Follower behavior modifier. @@ -85,6 +87,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) 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 a7baafe14..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,7 +1084,67 @@ 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, @@ -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; @@ -1264,24 +1335,19 @@ static boolean M_DrawFollowerSprite(INT16 x, INT16 y, INT32 num, boolean charfli static void M_DrawCharSelectSprite(UINT8 num, INT16 x, INT16 y, boolean charflip) { setup_player_t *p = &setup_player[num]; - UINT8 cnum = p->clonenum; - INT16 skin; 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; - skin = setup_chargrid[p->gridx][p->gridy].skinlist[cnum]; if (p->mdepth < CSSTEP_COLORS) - color = skins[skin].prefcolor; + color = skins[p->skin].prefcolor; else color = p->color; - colormap = R_GetTranslationColormap(skin, color, GTC_MENUCACHE); + colormap = R_GetTranslationColormap(p->skin, color, GTC_MENUCACHE); - if (skin >= 0) - M_DrawCharacterSprite(x, y, skin, charflip, (p->mdepth == CSSTEP_READY), 0, colormap); + M_DrawCharacterSprite(x, y, p->skin, charflip, (p->mdepth == CSSTEP_READY), 0, colormap); } static void M_DrawCharSelectPreview(UINT8 num) diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 8cd8ef7cc..f3d482eaa 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -2150,6 +2150,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: @@ -2578,6 +2579,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 +2702,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 +2734,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 +2751,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 +2792,100 @@ 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)) + { + p->followercategory = -1; + 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 +2895,15 @@ 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); @@ -2813,7 +2931,7 @@ 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); } @@ -2851,10 +2969,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) @@ -2903,6 +3035,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; @@ -3006,6 +3141,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; From fa5aa408be4ca09d5b12ddfe96d6e75317fc14cb Mon Sep 17 00:00:00 2001 From: toaster Date: Mon, 7 Nov 2022 00:58:55 +0000 Subject: [PATCH 08/12] WIP: Catch a few places which could be going wrong with followers on joining servers. Definitely better than it was, but current status: sometimes when joining a server cv_follower[n].value is 0 and I don't know why. --- src/d_netcmd.c | 12 ++++++----- src/k_follower.c | 56 +++++++++++++++++++++++++++--------------------- src/k_follower.h | 14 ++++++++++++ 3 files changed, 53 insertions(+), 29 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 1c527b1a5..f5df01d7a 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -1468,7 +1468,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) @@ -1501,7 +1501,7 @@ static void SendNameAndColor(UINT8 n) // 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"); + CV_StealthSet(&cv_follower[n], "None"); if (!strcmp(cv_playername[n].string, player_names[playernum]) && cv_playercolor[n].value == player->skincolor @@ -1587,7 +1587,8 @@ static void SendNameAndColor(UINT8 n) 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); @@ -1599,7 +1600,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; @@ -1628,7 +1629,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 diff --git a/src/k_follower.c b/src/k_follower.c index c6a9e97eb..b21f56595 100644 --- a/src/k_follower.c +++ b/src/k_follower.c @@ -77,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) @@ -85,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. @@ -97,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; @@ -256,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 4bf39dbc0..e0eddccd1 100644 --- a/src/k_follower.h +++ b/src/k_follower.h @@ -182,5 +182,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__ From 35a3a9db4ac66b1bc363194953dd5eca36207b54 Mon Sep 17 00:00:00 2001 From: toaster Date: Mon, 7 Nov 2022 16:42:22 +0000 Subject: [PATCH 09/12] Remove follower on player departure --- src/d_clisrv.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 1387c80e4..fc6612821 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -2487,6 +2487,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); From 2e750126450246b3a7be5274ecd429527bfe7412 Mon Sep 17 00:00:00 2001 From: toaster Date: Mon, 7 Nov 2022 18:11:06 +0000 Subject: [PATCH 10/12] Clean up follower setting to handle strings and values just like player->skin - Fixes defaulting to follower id 0 (previously chao, currently hot robo) when joining a server without going through the menu flow - No need to store TWO names for a follower, so save a little memory - Should make it easier to add unlockable followers later --- src/d_netcmd.c | 215 ++++++++--------------------------------------- src/deh_soc.c | 17 ++-- src/g_demo.c | 4 +- src/k_follower.c | 4 +- src/k_follower.h | 3 +- src/k_menufunc.c | 19 +++-- 6 files changed, 56 insertions(+), 206 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index f5df01d7a..9a60a7a0a 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -1528,31 +1528,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; } @@ -1582,6 +1579,13 @@ 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); @@ -6096,207 +6100,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 diff --git a/src/deh_soc.c b/src/deh_soc.c index 1d806322e..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]; @@ -3348,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 { @@ -3363,8 +3359,7 @@ void readfollower(MYFILE *f) // in that case, we'll be very lazy and copy numfollowers to the end of our skin name. } - 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++) @@ -3379,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); @@ -3411,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"); @@ -3422,7 +3417,7 @@ 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 diff --git a/src/g_demo.c b/src/g_demo.c index 551477d2e..bc461d84a 100644 --- a/src/g_demo.c +++ b/src/g_demo.c @@ -453,7 +453,7 @@ 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; @@ -2093,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. diff --git a/src/k_follower.c b/src/k_follower.c index b21f56595..636620ec8 100644 --- a/src/k_follower.c +++ b/src/k_follower.c @@ -31,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; } @@ -57,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; diff --git a/src/k_follower.h b/src/k_follower.h index e0eddccd1..3c1e2a7d1 100644 --- a/src/k_follower.h +++ b/src/k_follower.h @@ -45,8 +45,7 @@ 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 diff --git a/src/k_menufunc.c b/src/k_menufunc.c index f3d482eaa..4c3b6c5c1 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -3093,9 +3093,6 @@ 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]; @@ -3106,7 +3103,10 @@ static void M_MPConfirmCharacterSelection(void) CV_StealthSetValue(&cv_playercolor[i], col); // follower - CV_StealthSetValue(&cv_follower[i], setup_player[i].followern); + if (setup_player[i].followern < 0) + CV_StealthSet(&cv_follower[i], "None"); + else + CV_StealthSet(&cv_follower[i], followers[setup_player[i].followern].name); // follower color CV_StealthSetValue(&cv_followercolor[i], setup_player[i].followercolor); @@ -3117,8 +3117,8 @@ static void M_MPConfirmCharacterSelection(void) // This is a hack to make sure we call Skin[x]_OnChange afterwards CV_StealthSetValue(&cv_skin[i], -1); - strcpy(cmd, commandnames[i]); - strcat(cmd, skins[setup_player[i].skin].name); + strcpy(cmd, cv_skin[i].name); + strcat(cmd, va(" %s", skins[setup_player[i].skin].name)); COM_ImmedExecute(cmd); } @@ -3173,7 +3173,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 @@ -3190,7 +3190,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); } From e086b520551e6365c4f4ae29a0439b0d6563134b Mon Sep 17 00:00:00 2001 From: toaster Date: Mon, 7 Nov 2022 20:16:53 +0000 Subject: [PATCH 11/12] Fix the MP character select being completely busted and sending infinite changenameandcolor packets Still has weird inappropriate conditions for changing skin, but gets the feature at least WORKING for now. --- src/d_netcmd.c | 12 ++++++------ src/k_menufunc.c | 17 +++++++---------- 2 files changed, 13 insertions(+), 16 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 9a60a7a0a..247b3e151 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -1499,14 +1499,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], "None"); - 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; @@ -6060,6 +6057,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); @@ -6169,7 +6167,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/k_menufunc.c b/src/k_menufunc.c index 4c3b6c5c1..74135465d 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; @@ -3095,8 +3098,6 @@ static void M_MPConfirmCharacterSelection(void) 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; @@ -3108,18 +3109,14 @@ static void M_MPConfirmCharacterSelection(void) else CV_StealthSet(&cv_follower[i], followers[setup_player[i].followern].name); - // follower color - CV_StealthSetValue(&cv_followercolor[i], setup_player[i].followercolor); - // 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, cv_skin[i].name); - strcat(cmd, va(" %s", 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); From 6eda325561e768b20a13c2a50723a2260e940c10 Mon Sep 17 00:00:00 2001 From: toaster Date: Mon, 7 Nov 2022 20:57:02 +0000 Subject: [PATCH 12/12] More character select quality of life - When selecting follower category, initial follower category is now the category of your last used follower (or None) - More C/Extra button functionality: - Profile selection: Toggle between your last used profile and guest - Follower category: Toggle between category of your last selected follower and None - Follower: Return to category selection and hover over None (flows seamlessly into above option) --- src/k_menufunc.c | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 74135465d..4d5d401af 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -2066,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 @@ -2490,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; @@ -2863,7 +2879,10 @@ static void M_HandleFollowerCategoryRotate(setup_player_t *p, UINT8 num) } else if (M_MenuExtraPressed(num)) { - p->followercategory = -1; + 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 @@ -2938,6 +2957,15 @@ static void M_HandleFollowerRotate(setup_player_t *p, UINT8 num) 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)