From 5b13d4f75d6f772fc7869c09e04e166061861a1c Mon Sep 17 00:00:00 2001 From: toaster Date: Sat, 21 May 2022 19:35:40 +0100 Subject: [PATCH] Repair character colors and followercolors via the menu, both visually and mechanically. * Fully reimplement the MenuColor system from 2.2's codebase, so super and emerald colours are now inaccessible again. * Add FOLLOWERCOLOR_ constants and internal loop support to M_GetColorBefore and M_GetColorAfter. * Fix improper initialisation of certain menu colour data. * Repair previously created (or manually-edited) profiles with invalid colours. * Add an actual function to turn followercolor constants to effective values. --- src/doomdef.h | 4 ++ src/k_menu.h | 9 +-- src/k_menudraw.c | 133 +++++++++++-------------------------- src/k_menufunc.c | 164 ++++++++++++++++++++++++++++++++-------------- src/k_profiles.c | 19 ++++++ src/k_profiles.h | 2 +- src/lua_baselib.c | 17 ++++- src/p_user.c | 30 ++++----- src/r_data.h | 2 - 9 files changed, 207 insertions(+), 173 deletions(-) diff --git a/src/doomdef.h b/src/doomdef.h index 7e1213be2..070544900 100644 --- a/src/doomdef.h +++ b/src/doomdef.h @@ -226,6 +226,10 @@ typedef struct skincolor_s boolean accessible; // Accessible by the color command + setup menu } skincolor_t; +#define FOLLOWERCOLOR_MATCH UINT16_MAX +#define FOLLOWERCOLOR_OPPOSITE (UINT16_MAX-1) +UINT16 K_GetEffectiveFollowerColor(UINT16 followercolor, UINT16 playercolor); + typedef enum { SKINCOLOR_NONE = 0, diff --git a/src/k_menu.h b/src/k_menu.h index b39d1c872..b8648541d 100644 --- a/src/k_menu.h +++ b/src/k_menu.h @@ -488,11 +488,12 @@ void M_StopMessage(INT32 choice); void M_QuitResponse(INT32 ch); void M_QuitSRB2(INT32 choice); +extern UINT16 nummenucolors; void M_AddMenuColor(UINT16 color); void M_MoveColorBefore(UINT16 color, UINT16 targ); void M_MoveColorAfter(UINT16 color, UINT16 targ); -UINT16 M_GetColorBefore(UINT16 color); -UINT16 M_GetColorAfter(UINT16 color); +UINT16 M_GetColorBefore(UINT16 color, UINT16 amount, boolean follower); +UINT16 M_GetColorAfter(UINT16 color, UINT16 amount, boolean follower); void M_InitPlayerSetupColors(void); void M_FreePlayerSetupColors(void); @@ -525,7 +526,7 @@ typedef struct setup_player_s SINT8 clonenum; SINT8 rotate; UINT8 delay; - UINT8 color; + UINT16 color; UINT8 mdepth; // Hack, save player 1's original device even if they init charsel with keyboard. @@ -534,7 +535,7 @@ typedef struct setup_player_s UINT8 ponedevice; INT32 followern; - INT16 followercolor; + UINT16 followercolor; tic_t follower_tics; tic_t follower_timer; UINT8 follower_frame; diff --git a/src/k_menudraw.c b/src/k_menudraw.c index c1fd5f9aa..e443e88cc 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -818,15 +818,15 @@ void M_DrawImageDef(void) static void M_DrawCharSelectCircle(setup_player_t *p, INT16 x, INT16 y) { angle_t angamt = ANGLE_MAX; - UINT8 numoptions; - UINT8 i; + UINT16 i, numoptions; + UINT16 l = 0, r = 0; if (p->mdepth == CSSTEP_ALTS) numoptions = setup_chargrid[p->gridx][p->gridy].numskins; else if (p->mdepth == CSSTEP_FOLLOWERCOLORS) - numoptions = numskincolors-1 +2; + numoptions = nummenucolors+2; else - numoptions = numskincolors-1; + numoptions = nummenucolors; angamt /= numoptions; @@ -836,9 +836,9 @@ static void M_DrawCharSelectCircle(setup_player_t *p, INT16 x, INT16 y) boolean subtract = (i & 1); angle_t ang = ((i+1)/2) * angamt; patch_t *patch = NULL; - UINT8 *colormap; - fixed_t radius; - INT16 n; + UINT8 *colormap = NULL; + fixed_t radius = 28<mdepth == CSSTEP_ALTS) { @@ -865,36 +865,24 @@ static void M_DrawCharSelectCircle(setup_player_t *p, INT16 x, INT16 y) INT16 diff; UINT16 col; - n = (p->followercolor-1) + numoptions/2; - - if (subtract) - n -= ((i+1)/2); - else - n += ((i+1)/2); - - n %= numoptions; - n++; - - if (!n) - continue; - - col = (unsigned)(n); - switch (col) + if (i == 0) { - case FOLLOWERCOLOR_MATCH: // "Match" - col = p->color; - break; - case FOLLOWERCOLOR_OPPOSITE: // "Opposite" - col = skincolors[p->color].invcolor; - break; + n = l = r = M_GetColorBefore(p->followercolor, numoptions/2, true); } + else if (subtract) + { + n = l = M_GetColorBefore(l, 1, true); + } + else + { + n = r = M_GetColorAfter(r, 1, true); + } + + col = K_GetEffectiveFollowerColor(n, p->color); colormap = R_GetTranslationColormap(TC_DEFAULT, col, GTC_MENUCACHE); - if (n > p->followercolor) - diff = n - p->followercolor; - else - diff = p->followercolor - n; + diff = (numoptions - i)/2; // only 0 when i == numoptions-1 if (diff == 0) patch = W_CachePatchName("COLORSP2", PU_CACHE); @@ -903,29 +891,28 @@ static void M_DrawCharSelectCircle(setup_player_t *p, INT16 x, INT16 y) else patch = W_CachePatchName("COLORSP0", PU_CACHE); - radius = 28<width) << FRACBITS; - cx -= (SHORT(patch->width) << FRACBITS) >> 1; } else { INT16 diff; - n = (p->color-1) + numoptions/2; - if (subtract) - n -= ((i+1)/2); + if (i == 0) + { + n = l = r = M_GetColorBefore(p->color, numoptions/2, false); + } + else if (subtract) + { + n = l = M_GetColorBefore(l, 1, false); + } else - n += ((i+1)/2); - n %= numoptions; - n++; + { + n = r = M_GetColorAfter(r, 1, false); + } colormap = R_GetTranslationColormap(TC_DEFAULT, n, GTC_MENUCACHE); - if (n > p->color) - diff = n - p->color; - else - diff = p->color - n; + diff = (numoptions - i)/2; // only 0 when i == numoptions-1 if (diff == 0) patch = W_CachePatchName("COLORSP2", PU_CACHE); @@ -934,9 +921,6 @@ static void M_DrawCharSelectCircle(setup_player_t *p, INT16 x, INT16 y) else patch = W_CachePatchName("COLORSP0", PU_CACHE); - radius = 28<width) << FRACBITS; - cx -= (SHORT(patch->width) << FRACBITS) >> 1; } @@ -945,7 +929,7 @@ static void M_DrawCharSelectCircle(setup_player_t *p, INT16 x, INT16 y) else ang = ANGLE_90 + ang; - if (numoptions % 2) + if (numoptions & 1) ang = (signed)(ang - (angamt/2)); if (p->rotate) @@ -1034,19 +1018,9 @@ static void M_DrawFollowerList(setup_player_t *p, UINT8 num) if (W_LumpExists(fl.icon) && cf >= 0) { - UINT16 col = (unsigned)p->followercolor; + UINT16 col = K_GetEffectiveFollowerColor(p->followercolor, p->color); pp = W_CachePatchName(fl.icon, PU_CACHE); - switch (col) - { - case FOLLOWERCOLOR_MATCH: // "Match" - col = p->color; - break; - case FOLLOWERCOLOR_OPPOSITE: // "Opposite" - col = skincolors[p->color].invcolor; - break; - } - colormap = R_GetTranslationColormap(TC_DEFAULT, col, GTC_MENUCACHE); V_DrawMappedPatch(x, y, 0, pp, colormap); if (i == 1) @@ -1137,19 +1111,7 @@ static boolean M_DrawFollowerSprite(INT16 x, INT16 y, INT32 num, INT32 addflags, { const fixed_t pi = (22<follower_timer)>>ANGLETOFINESHIFT) & FINEMASK); - color = p->followercolor; - - switch (color) - { - case FOLLOWERCOLOR_MATCH: // "Match" - color = p->color; - break; - case FOLLOWERCOLOR_OPPOSITE: // "Opposite" - color = skincolors[p->color].invcolor; - break; - default: - break; - } + color = K_GetEffectiveFollowerColor(p->followercolor, p->color); } colormap = R_GetTranslationColormap(TC_DEFAULT, color, GTC_MENUCACHE); @@ -1405,20 +1367,10 @@ static void M_DrawProfileCard(INT32 x, INT32 y, boolean greyedout, profile_t *p) { if (M_DrawFollowerSprite(x-44 +12, y+119, 0, V_FLIP, 0, sp)) { - UINT16 col = (unsigned)p->followercolor; + UINT16 col = K_GetEffectiveFollowerColor(sp->followercolor, sp->color);; patch_t *ico = W_CachePatchName(followers[sp->followern].icon, PU_CACHE); UINT8 *fcolormap; - switch (col) - { - case FOLLOWERCOLOR_MATCH: // "Match" - col = sp->color; - break; - case FOLLOWERCOLOR_OPPOSITE: // "Opposite" - col = skincolors[sp->color].invcolor; - break; - } - fcolormap = R_GetTranslationColormap(TC_DEFAULT, col, GTC_MENUCACHE); V_DrawMappedPatch(x+14+18, y+66, 0, ico, fcolormap); } @@ -1433,23 +1385,12 @@ static void M_DrawProfileCard(INT32 x, INT32 y, boolean greyedout, profile_t *p) else if (skinnum > -1) // otherwise, read from profile. { - UINT16 col = (unsigned)p->followercolor; + UINT16 col = K_GetEffectiveFollowerColor(p->followercolor, p->color);; UINT8 fln = R_FollowerAvailable(p->follower); if (M_DrawCharacterSprite(x-22, y+119, skinnum, V_FLIP, colormap)) V_DrawMappedPatch(x+14, y+66, 0, faceprefix[skinnum][FACE_RANK], colormap); - switch (col) - { - case FOLLOWERCOLOR_MATCH: // "Match" - col = p->color; - break; - case FOLLOWERCOLOR_OPPOSITE: // "Opposite" - col = skincolors[p->color].invcolor; - break; - } - - if (M_DrawFollowerSprite(x-44 +12, y+119, fln, V_FLIP, col, NULL)) { patch_t *ico = W_CachePatchName(followers[fln].icon, PU_CACHE); diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 514109dd7..394d8d80b 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -501,16 +501,18 @@ void M_EraseData(INT32 choice) // BASIC MENU HANDLING // ========================================================================= +UINT16 nummenucolors = 0; + void M_AddMenuColor(UINT16 color) { menucolor_t *c; - // SRB2Kart: I do not understand vanilla doesn't need this but WE do???!?!??! - if (!skincolors[color].accessible) { + if (color >= numskincolors) { + CONS_Printf("M_AddMenuColor: color %d does not exist.",color); return; } - if (color >= numskincolors) { - CONS_Printf("M_AddMenuColor: color %d does not exist.",color); + // SRB2Kart: I do not understand vanilla doesn't need this but WE do???!?!??! + if (!skincolors[color].accessible) { return; } @@ -528,6 +530,8 @@ void M_AddMenuColor(UINT16 color) { menucolorhead->prev = c; menucolortail = c; } + + nummenucolors++; } void M_MoveColorBefore(UINT16 color, UINT16 targ) { @@ -614,36 +618,118 @@ void M_MoveColorAfter(UINT16 color, UINT16 targ) { t->next = c; } -UINT16 M_GetColorBefore(UINT16 color) { - menucolor_t *look; +UINT16 M_GetColorBefore(UINT16 color, UINT16 amount, boolean follower) +{ + menucolor_t *look = NULL; - if (color >= numskincolors) { - CONS_Printf("M_GetColorBefore: color %d does not exist.\n",color); - return 0; - } + for (; amount > 0; amount--) + { + if (follower == true) + { + if (color == FOLLOWERCOLOR_OPPOSITE) + { + look = menucolortail; + color = menucolortail->color; + continue; + } - for (look=menucolorhead;;look=look->next) { - if (look->color == color) - return look->prev->color; - if (look==menucolortail) + if (color == FOLLOWERCOLOR_MATCH) + { + look = NULL; + color = FOLLOWERCOLOR_OPPOSITE; + continue; + } + + if (color == menucolorhead->color) + { + look = NULL; + color = FOLLOWERCOLOR_MATCH; + continue; + } + } + + if (color == 0 || color >= numskincolors) + { + CONS_Printf("M_GetColorBefore: color %d does not exist.\n",color); return 0; + } + + if (look == NULL) + { + for (look = menucolorhead;; look = look->next) + { + if (look->color == color) + { + break; + } + if (look == menucolortail) + { + return 0; + } + } + } + + look = look->prev; + color = look->color; } + return color; } -UINT16 M_GetColorAfter(UINT16 color) { - menucolor_t *look; +UINT16 M_GetColorAfter(UINT16 color, UINT16 amount, boolean follower) +{ + menucolor_t *look = NULL; - if (color >= numskincolors) { - CONS_Printf("M_GetColorAfter: color %d does not exist.\n",color); - return 0; - } + for (; amount > 0; amount--) + { + if (follower == true) + { + if (color == menucolortail->color) + { + look = NULL; + color = FOLLOWERCOLOR_OPPOSITE; + continue; + } - for (look=menucolorhead;;look=look->next) { - if (look->color == color) - return look->next->color; - if (look==menucolortail) + if (color == FOLLOWERCOLOR_OPPOSITE) + { + look = NULL; + color = FOLLOWERCOLOR_MATCH; + continue; + } + + if (color == FOLLOWERCOLOR_MATCH) + { + look = menucolorhead; + color = menucolorhead->color; + continue; + } + } + + if (color == 0 || color >= numskincolors) + { + CONS_Printf("M_GetColorAfter: color %d does not exist.\n",color); return 0; + } + + if (look == NULL) + { + for (look = menucolorhead;; look = look->next) + { + if (look->color == color) + { + break; + } + if (look == menucolortail) + { + return 0; + } + } + } + + look = look->next; + color = look->color; } + return color; } void M_InitPlayerSetupColors(void) { @@ -2126,7 +2212,7 @@ void M_CharacterSelectInit(void) { // Default to no follower / match colour. setup_player[i].followern = -1; - setup_player[i].followercolor = -1; + setup_player[i].followercolor = FOLLOWERCOLOR_MATCH; // Set default selected profile to the last used profile for each player: // (Make sure we don't overshoot it somehow if we deleted profiles or whatnot) @@ -2576,18 +2662,14 @@ static void M_HandleColorRotate(setup_player_t *p, UINT8 num) if (menucmd[num].dpad_lr > 0) { - p->color++; - if (p->color >= numskincolors) - p->color = 1; + p->color = M_GetColorAfter(p->color, 1, false); p->rotate = CSROTATETICS; M_SetMenuDelay(num); //CSROTATETICS S_StartSound(NULL, sfx_s3k5b); //sfx_s3kc3s } else if (menucmd[num].dpad_lr < 0) { - p->color--; - if (p->color < 1) - p->color = numskincolors-1; + p->color = M_GetColorBefore(p->color, 1, false); p->rotate = -CSROTATETICS; M_SetMenuDelay(num); //CSROTATETICS S_StartSound(NULL, sfx_s3k5b); //sfx_s3kc3s @@ -2696,7 +2778,6 @@ static void M_HandleChooseFollower(setup_player_t *p, UINT8 num) static void M_HandleFollowerColorRotate(setup_player_t *p, UINT8 num) { - if (cv_splitdevice.value) num = 0; @@ -2704,29 +2785,14 @@ static void M_HandleFollowerColorRotate(setup_player_t *p, UINT8 num) if (menucmd[num].dpad_lr > 0) { - p->followercolor++; - - // Go back to -2 (Opposite) - if (p->followercolor >= numskincolors) - p->followercolor = -2; - - // Make sure we skip 0. - if (p->followercolor == 0) - p->followercolor++; - + p->followercolor = M_GetColorAfter(p->followercolor, 1, true); p->rotate = CSROTATETICS; M_SetMenuDelay(num); //CSROTATETICS S_StartSound(NULL, sfx_s3k5b); //sfx_s3kc3s } else if (menucmd[num].dpad_lr < 0) { - p->followercolor--; - if (p->followercolor < -2) - p->followercolor = numskincolors-1; - - if (p->followercolor == 0) - p->followercolor--; - + p->followercolor = M_GetColorBefore(p->followercolor, 1, true); p->rotate = -CSROTATETICS; M_SetMenuDelay(num); //CSROTATETICS S_StartSound(NULL, sfx_s3k5b); //sfx_s3kc3s diff --git a/src/k_profiles.c b/src/k_profiles.c index 593d464f4..14fd53885 100644 --- a/src/k_profiles.c +++ b/src/k_profiles.c @@ -199,6 +199,25 @@ void PR_LoadProfiles(void) { profilesList[i] = Z_Malloc(sizeof(profile_t), PU_STATIC, NULL); fread(profilesList[i], sizeof(profile_t), 1, f); + + // Attempt to correct numerical footguns + if (profilesList[i]->color >= numskincolors + || profilesList[i]->color == 0 + || skincolors[profilesList[i]->color].accessible == false) + { + profilesList[i]->color = PROFILEDEFAULTCOLOR; + } + if (profilesList[i]->followercolor == FOLLOWERCOLOR_MATCH + || profilesList[i]->followercolor == FOLLOWERCOLOR_OPPOSITE) + { + ; // Valid, even outside the bounds + } + else if (profilesList[i]->followercolor >= numskincolors + || profilesList[i]->followercolor == 0 + || skincolors[profilesList[i]->followercolor].accessible == false) + { + profilesList[i]->followercolor = PROFILEDEFAULTFOLLOWERCOLOR; + } } fclose(f); diff --git a/src/k_profiles.h b/src/k_profiles.h index 92c0797f1..c65b3aed6 100644 --- a/src/k_profiles.h +++ b/src/k_profiles.h @@ -35,7 +35,7 @@ #define PROFILEDEFAULTSKIN "sonic" #define PROFILEDEFAULTCOLOR SKINCOLOR_SAPPHIRE #define PROFILEDEFAULTFOLLOWER "none" -#define PROFILEDEFAULTFOLLOWERCOLOR 255 +#define PROFILEDEFAULTFOLLOWERCOLOR FOLLOWERCOLOR_MATCH // Man I wish I had more than 16 friends!! diff --git a/src/lua_baselib.c b/src/lua_baselib.c index 319448593..510a6f340 100644 --- a/src/lua_baselib.c +++ b/src/lua_baselib.c @@ -354,14 +354,26 @@ static int lib_pMoveColorAfter(lua_State *L) static int lib_pGetColorBefore(lua_State *L) { UINT16 color = (UINT16)luaL_checkinteger(L, 1); - lua_pushinteger(L, M_GetColorBefore(color)); + UINT16 amount = (UINT16)luaL_checkinteger(L, 2); + boolean follower = lua_optboolean(L, 3); + lua_pushinteger(L, M_GetColorBefore(color, amount, follower)); return 1; } static int lib_pGetColorAfter(lua_State *L) { UINT16 color = (UINT16)luaL_checkinteger(L, 1); - lua_pushinteger(L, M_GetColorAfter(color)); + UINT16 amount = (UINT16)luaL_checkinteger(L, 2); + boolean follower = lua_optboolean(L, 3); + lua_pushinteger(L, M_GetColorAfter(color, amount, follower)); + return 1; +} + +static int lib_pGetEffectiveFollowerColor(lua_State *L) +{ + UINT16 followercolor = (UINT16)luaL_checkinteger(L, 1); + UINT16 playercolor = (UINT16)luaL_checkinteger(L, 2); + lua_pushinteger(L, K_GetEffectiveFollowerColor(followercolor, playercolor)); return 1; } @@ -3926,6 +3938,7 @@ static luaL_Reg lib[] = { {"P_ReturnThrustX",lib_pReturnThrustX}, {"P_ReturnThrustY",lib_pReturnThrustY}, {"P_NukeEnemies",lib_pNukeEnemies}, + {"K_GetEffectiveFollowerColor",lib_pGetEffectiveFollowerColor}, // p_map {"P_CheckPosition",lib_pCheckPosition}, diff --git a/src/p_user.c b/src/p_user.c index d2ca62ee9..8c1bc28b8 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -3948,6 +3948,16 @@ static void P_SetFollowerState(mobj_t *f, INT32 state) f->tics++; } +UINT16 K_GetEffectiveFollowerColor(UINT16 followercolor, UINT16 playercolor) +{ + if (followercolor < numskincolors) // bog standard + return followercolor; + if (followercolor == FOLLOWERCOLOR_OPPOSITE) // "Opposite" + return skincolors[playercolor].invcolor; + //if (followercolor == FOLLOWERCOLOR_MATCH) -- "Match" + return playercolor; +} + // //P_HandleFollower // @@ -4007,25 +4017,7 @@ static void P_HandleFollower(player_t *player) sz += FixedMul(player->mo->scale, sine)*P_MobjFlip(player->mo); } - // Set follower colour - switch (player->followercolor) - { - case FOLLOWERCOLOR_MATCH: // "Match" - color = player->skincolor; - break; - case FOLLOWERCOLOR_OPPOSITE: // "Opposite" - color = skincolors[player->skincolor].invcolor; - break; - default: - - color = player->followercolor; - if (!color || color > MAXSKINCOLORS+2) // Make sure this isn't garbage - color = player->skincolor; // "Match" as fallback. - - break; - } - - + color = K_GetEffectiveFollowerColor(player->followercolor, player->skincolor); if (!player->follower) // follower doesn't exist / isn't valid { diff --git a/src/r_data.h b/src/r_data.h index 1228f2420..be92c094e 100644 --- a/src/r_data.h +++ b/src/r_data.h @@ -41,8 +41,6 @@ extern INT16 *hicolormaps; // remap high colors to high colors.. extern CV_PossibleValue_t Color_cons_t[]; extern CV_PossibleValue_t Followercolor_cons_t[]; // follower colours table, not a duplicate because of the "Match" option. -#define FOLLOWERCOLOR_MATCH UINT16_MAX -#define FOLLOWERCOLOR_OPPOSITE (UINT16_MAX-1) // I/O, setting up the stuff. void R_InitTextureData(void);