diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 396478520..45b66b1f9 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -316,10 +316,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", "Default", CV_SAVE|CV_CALL|CV_NOINIT, Followercolor_cons_t, Followercolor_OnChange), + CVAR_INIT ("followercolor2", "Default", CV_SAVE|CV_CALL|CV_NOINIT, Followercolor_cons_t, Followercolor2_OnChange), + CVAR_INIT ("followercolor3", "Default", CV_SAVE|CV_CALL|CV_NOINIT, Followercolor_cons_t, Followercolor3_OnChange), + CVAR_INIT ("followercolor4", "Default", CV_SAVE|CV_CALL|CV_NOINIT, Followercolor_cons_t, Followercolor4_OnChange) }; consvar_t cv_skipmapcheck = CVAR_INIT ("skipmapcheck", "Off", CV_SAVE, CV_OnOff, NULL); @@ -855,17 +855,20 @@ void D_RegisterClientCommands(void) Color_cons_t[i].strvalue = skincolors[i].name; } - for (i = 2; i < MAXSKINCOLORS; i++) + for (i = 3; i < MAXSKINCOLORS; i++) { - Followercolor_cons_t[i].value = i-2; - Followercolor_cons_t[i].strvalue = skincolors[i-2].name; + Followercolor_cons_t[i].value = i-3; + Followercolor_cons_t[i].strvalue = skincolors[i-3].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[2].value = FOLLOWERCOLOR_MATCH; + Followercolor_cons_t[2].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. + Followercolor_cons_t[1].value = FOLLOWERCOLOR_OPPOSITE; + Followercolor_cons_t[1].strvalue = "Opposite"; // Add "Opposite" option, ...which is like "Match", but for coloropposite. + + Followercolor_cons_t[0].value = FOLLOWERCOLOR_DEFAULT; + Followercolor_cons_t[0].strvalue = "Default"; // Add "Default" option, which will use the default color of the selected follower Color_cons_t[MAXSKINCOLORS].value = Followercolor_cons_t[MAXSKINCOLORS+2].value = 0; Color_cons_t[MAXSKINCOLORS].strvalue = Followercolor_cons_t[MAXSKINCOLORS+2].strvalue = NULL; @@ -1516,7 +1519,7 @@ static void SendNameAndColor(UINT8 n) // ditto for follower colour: if (!cv_followercolor[n].value) - CV_StealthSet(&cv_followercolor[n], "Match"); // set it to "Match". I don't care about your stupidity! + CV_StealthSet(&cv_followercolor[n], "Default"); // set it to "Default". 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) diff --git a/src/deh_soc.c b/src/deh_soc.c index a16ff32d6..e3664e92b 100644 --- a/src/deh_soc.c +++ b/src/deh_soc.c @@ -3684,7 +3684,7 @@ void readfollower(MYFILE *f) followers[numfollowers].horzlag = 3*FRACUNIT; followers[numfollowers].vertlag = 6*FRACUNIT; followers[numfollowers].anglelag = 8*FRACUNIT; - followers[numfollowers].bobspeed = TICRATE*2; + followers[numfollowers].bobspeed = (TICRATE*2)*FRACUNIT; followers[numfollowers].bobamp = 4*FRACUNIT; followers[numfollowers].hitconfirmtime = TICRATE; followers[numfollowers].defaultcolor = SKINCOLOR_GREEN; @@ -3735,7 +3735,15 @@ void readfollower(MYFILE *f) } else if (fastcmp(word, "DEFAULTCOLOR")) { - followers[numfollowers].defaultcolor = get_number(word2); + if (word2) + strupr(word2); + + if (fastcmp(word2, "MATCH")) + followers[numfollowers].defaultcolor = FOLLOWERCOLOR_MATCH; + else if (fastcmp(word2, "OPPOSITE")) + followers[numfollowers].defaultcolor = FOLLOWERCOLOR_OPPOSITE; + else + followers[numfollowers].defaultcolor = get_skincolor(word2); } else if (fastcmp(word, "SCALE")) { @@ -3894,9 +3902,11 @@ if ((signed)followers[numfollowers].field < threshold) \ #undef FALLBACK // Special case for color I suppose - if (followers[numfollowers].defaultcolor > (unsigned)(numskincolors-1)) + if (followers[numfollowers].defaultcolor > (unsigned)(numskincolors-1) + && followers[numfollowers].defaultcolor != FOLLOWERCOLOR_MATCH + && followers[numfollowers].defaultcolor != FOLLOWERCOLOR_OPPOSITE) { - followers[numfollowers].defaultcolor = SKINCOLOR_GREEN; + followers[numfollowers].defaultcolor = FOLLOWERCOLOR_MATCH; deh_warning("Follower \'%s\': Value for 'color' should be between 1 and %d.\n", dname, numskincolors-1); } diff --git a/src/k_follower.c b/src/k_follower.c index 53c211458..d9facdcf6 100644 --- a/src/k_follower.c +++ b/src/k_follower.c @@ -175,6 +175,43 @@ static void K_SetFollowerState(mobj_t *f, statenum_t state) } } +/*-------------------------------------------------- + UINT16 K_GetEffectiveFollowerColor(UINT16 followercolor, follower_t *follower, UINT16 playercolor, skin_t *playerskin) + + See header file for description. +--------------------------------------------------*/ +UINT16 K_GetEffectiveFollowerColor(UINT16 followercolor, follower_t *follower, UINT16 playercolor, skin_t *playerskin) +{ + if (followercolor == SKINCOLOR_NONE && follower != NULL) // "Default" + { + followercolor = follower->defaultcolor; + } + + if (followercolor > SKINCOLOR_NONE && followercolor < numskincolors) // bog standard + { + return followercolor; + } + + if (playercolor == SKINCOLOR_NONE) // get default color + { + if (playerskin == NULL) + { + // Nothing from this line down is valid if playerskin is invalid, just guess Eggman? + playerskin = &skins[0]; + } + + playercolor = playerskin->prefcolor; + } + + if (followercolor == FOLLOWERCOLOR_OPPOSITE) // "Opposite" + { + return skincolors[playercolor].invcolor; + } + + // "Match" + return playercolor; +} + /*-------------------------------------------------- static void K_UpdateFollowerState(mobj_t *f, statenum_t state, followerstate_t type) @@ -312,15 +349,20 @@ void K_HandleFollower(player_t *player) color = skincolors[player->skincolor].invcolor; break; + case FOLLOWERCOLOR_DEFAULT: // "Default" + color = fl.defaultcolor; + break; + default: color = player->followercolor; - if (color == 0 || color > MAXSKINCOLORS+2) // Make sure this isn't garbage - { - color = player->skincolor; // "Match" as fallback. - } break; } + if (color == 0 || color > MAXSKINCOLORS+2) // Make sure this isn't garbage + { + color = player->skincolor; // "Match" as fallback. + } + if (player->follower == NULL) // follower doesn't exist / isn't valid { //CONS_Printf("Spawning follower...\n"); diff --git a/src/k_follower.h b/src/k_follower.h index 0466bdfac..77f1c9377 100644 --- a/src/k_follower.h +++ b/src/k_follower.h @@ -19,6 +19,7 @@ #define FOLLOWERCOLOR_MATCH UINT16_MAX #define FOLLOWERCOLOR_OPPOSITE (UINT16_MAX-1) +#define FOLLOWERCOLOR_DEFAULT 0 extern CV_PossibleValue_t Followercolor_cons_t[]; // follower colours table, not a duplicate because of the "Match" option. @@ -136,6 +137,25 @@ boolean K_SetFollowerByName(INT32 playernum, const char *skinname); void K_SetFollowerByNum(INT32 playernum, INT32 skinnum); +/*-------------------------------------------------- + UINT16 K_GetEffectiveFollowerColor(UINT16 followercolor, follower_t *follower, UINT16 playercolor, skin_t *playerskin) + + Updates a player's follower pointer, and does + its positioning and animations. + + Input Arguments:- + followercolor - The raw color setting for the follower + follower - Follower struct to retrieve default color from. Can be NULL + playercolor - The player's associated colour, for reference + playerskin - Skin struct to retrieve default color from. Can be NULL + + Return:- + The resultant skincolor enum for the follower +--------------------------------------------------*/ + +UINT16 K_GetEffectiveFollowerColor(UINT16 followercolor, follower_t *follower, UINT16 playercolor, skin_t *playerskin); + + /*-------------------------------------------------- void K_HandleFollower(player_t *player) diff --git a/src/m_menu.c b/src/m_menu.c index 53355c371..c61514603 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -9404,7 +9404,7 @@ static state_t *multi_state; static UINT8 multi_spr2; // used for follower display on player setup menu -static INT32 follower_tics; +static fixed_t follower_tics; static UINT32 follower_frame; // used for FF_ANIMATE garbo static state_t *follower_state; @@ -9416,6 +9416,7 @@ static consvar_t *setupm_cvskin; static consvar_t *setupm_cvcolor; static consvar_t *setupm_cvname; static consvar_t *setupm_cvfollower; +static consvar_t *setupm_cvfollowercolor; static INT32 setupm_fakeskin; static menucolor_t *setupm_fakecolor; static INT32 setupm_fakefollower; // -1 is for none, our followers start at 0 @@ -9679,14 +9680,15 @@ static void M_DrawSetupMultiPlayerMenu(void) { // animate the follower - if (--follower_tics <= 0) + follower_tics -= renderdeltatics; + if (follower_tics <= 0) { // FF_ANIMATE; cycle through FRAMES and get back afterwards. This will be prominent amongst followers hence why it's being supported here. if (follower_state->frame & FF_ANIMATE) { follower_frame++; - follower_tics = follower_state->var2; + follower_tics = follower_state->var2*FRACUNIT; if (follower_frame > (follower_state->frame & FF_FRAMEMASK) + follower_state->var1) // that's how it works, right? follower_frame = follower_state->frame & FF_FRAMEMASK; } @@ -9695,9 +9697,7 @@ static void M_DrawSetupMultiPlayerMenu(void) st = follower_state->nextstate; if (st != S_NULL) follower_state = &states[st]; - follower_tics = follower_state->tics; - if (follower_tics == -1) - follower_tics = 15; // er, what? + follower_tics = follower_state->tics*FRACUNIT; // get spritedef: follower_frame = follower_state->frame & FF_FRAMEMASK; } @@ -9713,20 +9713,30 @@ static void M_DrawSetupMultiPlayerMenu(void) if (sprframe->flip & 2) // Only for first sprite flags |= V_FLIP; // This sprite is left/right flipped! - // @TODO: Reminder that followers on the menu right now do NOT support the 'followercolor' command, considering this whole menu is getting remade anyway, I see no point in incorporating it in right now. - // draw follower sprite if (setupm_fakecolor->color) // inverse should never happen { - // Fake the follower's in game appearance by now also applying some of its variables! coolio, eh? follower_t fl = followers[setupm_fakefollower]; // shortcut for our sanity - // smooth floating, totally not stolen from rocket sneakers. - fixed_t sine = FixedMul(fl.bobamp, FINESINE(((FixedMul(4 * M_TAU_FIXED, fl.bobspeed) * followertimer)>>ANGLETOFINESHIFT) & FINEMASK)); + tic_t bobspeed = fl.bobspeed; + if (fl.mode == FOLLOWERMODE_GROUND) + bobspeed = FixedDiv(bobspeed, fl.bobamp / 6); // Rough approximation of bounce speed - UINT8 *colormap = R_GetTranslationColormap(TC_DEFAULT, setupm_fakecolor->color, 0); // why does GTC_MENUCACHE not work here...? - V_DrawFixedPatch((mx+65)*FRACUNIT, ((my+131)*FRACUNIT)-fl.zoffs+sine, fl.scale, flags, patch, colormap); + // smooth floating, totally not stolen from rocket sneakers. + fixed_t sine = FixedMul(fl.bobamp, FINESINE(((FixedMul(4 * M_TAU_FIXED, bobspeed) * followertimer)>>ANGLETOFINESHIFT) & FINEMASK)); + + UINT16 color = K_GetEffectiveFollowerColor(setupm_cvfollowercolor->value, &fl, setupm_fakecolor->color, &skins[setupm_fakeskin]); + UINT8 *colormap = R_GetTranslationColormap(TC_DEFAULT, color, 0); // why does GTC_MENUCACHE not work here...? + + INT32 x = (mx+65)*FRACUNIT; + INT32 y = ((my+100)*FRACUNIT); + if (fl.mode == FOLLOWERMODE_GROUND) + y += 40*FRACUNIT - abs(sine) * 2; // Bounce animation + else + y += sine; + + V_DrawFixedPatch(x, y, fl.scale, flags, patch, colormap); Z_Free(colormap); } } @@ -9748,9 +9758,9 @@ static void M_GetFollowerState(void) follower_state = &states[followers[setupm_fakefollower].followstate]; if (follower_state->frame & FF_ANIMATE) - follower_tics = follower_state->var2; // support for FF_ANIMATE + follower_tics = follower_state->var2*FRACUNIT; // support for FF_ANIMATE else - follower_tics = follower_state->tics; + follower_tics = follower_state->tics*FRACUNIT; follower_frame = follower_state->frame & FF_FRAMEMASK; } @@ -9930,6 +9940,7 @@ static void M_SetupMultiPlayer(INT32 choice) setupm_cvcolor = &cv_playercolor[0]; setupm_cvname = &cv_playername[0]; setupm_cvfollower = &cv_follower[0]; + setupm_cvfollowercolor = &cv_followercolor[0]; setupm_fakefollower = setupm_cvfollower->value; @@ -9973,6 +9984,7 @@ static void M_SetupMultiPlayer2(INT32 choice) setupm_cvcolor = &cv_playercolor[1]; setupm_cvname = &cv_playername[1]; setupm_cvfollower = &cv_follower[1]; + setupm_cvfollowercolor = &cv_followercolor[1]; setupm_fakefollower = setupm_cvfollower->value; @@ -10016,6 +10028,7 @@ static void M_SetupMultiPlayer3(INT32 choice) setupm_cvcolor = &cv_playercolor[2]; setupm_cvname = &cv_playername[2]; setupm_cvfollower = &cv_follower[2]; + setupm_cvfollowercolor = &cv_followercolor[2]; setupm_fakefollower = setupm_cvfollower->value; @@ -10059,6 +10072,7 @@ static void M_SetupMultiPlayer4(INT32 choice) setupm_cvcolor = &cv_playercolor[3]; setupm_cvname = &cv_playername[3]; setupm_cvfollower = &cv_follower[3]; + setupm_cvfollowercolor = &cv_followercolor[3]; setupm_fakefollower = setupm_cvfollower->value;