From fe3852f005951f1290f5677938b5a5c28b990918 Mon Sep 17 00:00:00 2001 From: Latapostrophe Date: Wed, 13 Feb 2019 10:06:52 +0100 Subject: [PATCH] I needed to commit here so I could switch branches to fix magnet hand xd --- src/d_netcmd.c | 129 +++++++++++++++++++++++++++++++++++--- src/d_netcmd.h | 4 ++ src/d_player.h | 1 + src/dehacked.c | 145 ++++++++++++++++++++++++++++++++++++------- src/doomdef.h | 2 +- src/m_menu.c | 163 +++++++++++++++++++++++++++++++++++++++++++++++-- src/r_things.c | 64 ++++++++++++++++++- src/r_things.h | 15 +++-- 8 files changed, 483 insertions(+), 40 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 438cdcd5..c3596f05 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -103,6 +103,10 @@ static void Skin_OnChange(void); static void Skin2_OnChange(void); static void Skin3_OnChange(void); static void Skin4_OnChange(void); +static void Follower_OnChange(void); +static void Follower2_OnChange(void); +static void Follower3_OnChange(void); +static void Follower4_OnChange(void); static void Color_OnChange(void); static void Color2_OnChange(void); static void Color3_OnChange(void); @@ -268,6 +272,12 @@ consvar_t cv_skin2 = {"skin2", DEFAULTSKIN2, CV_SAVE|CV_CALL|CV_NOINIT, NULL, Sk consvar_t cv_skin3 = {"skin3", DEFAULTSKIN3, CV_SAVE|CV_CALL|CV_NOINIT, NULL, Skin3_OnChange, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_skin4 = {"skin4", DEFAULTSKIN4, CV_SAVE|CV_CALL|CV_NOINIT, NULL, Skin4_OnChange, 0, NULL, NULL, 0, 0, NULL}; +// player's followers. Also saved. +consvar_t cv_follower = {"follower", "-1", CV_SAVE|CV_CALL|CV_NOINIT, NULL, Follower_OnChange, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_follower2 = {"follower2", "-1", CV_SAVE|CV_CALL|CV_NOINIT, NULL, Follower2_OnChange, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_follower3 = {"follower3", "-1", CV_SAVE|CV_CALL|CV_NOINIT, NULL, Follower3_OnChange, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_follower4 = {"follower4", "-1", CV_SAVE|CV_CALL|CV_NOINIT, NULL, Follower4_OnChange, 0, NULL, NULL, 0, 0, NULL}; + consvar_t cv_skipmapcheck = {"skipmapcheck", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; INT32 cv_debug; @@ -747,18 +757,22 @@ void D_RegisterClientCommands(void) CV_RegisterVar(&cv_playername); CV_RegisterVar(&cv_playercolor); CV_RegisterVar(&cv_skin); // r_things.c (skin NAME) + CV_RegisterVar(&cv_follower); // secondary player (splitscreen) CV_RegisterVar(&cv_playername2); CV_RegisterVar(&cv_playercolor2); CV_RegisterVar(&cv_skin2); + CV_RegisterVar(&cv_follower2); // third player CV_RegisterVar(&cv_playername3); CV_RegisterVar(&cv_playercolor3); CV_RegisterVar(&cv_skin3); + CV_RegisterVar(&cv_follower3); // fourth player CV_RegisterVar(&cv_playername4); CV_RegisterVar(&cv_playercolor4); CV_RegisterVar(&cv_skin4); + CV_RegisterVar(&cv_follower4); // preferred number of players CV_RegisterVar(&cv_splitplayers); @@ -1241,9 +1255,9 @@ static INT32 snacpending = 0, snac2pending = 0, snac3pending = 0, snac4pending = // static void SendNameAndColor(void) { - XBOXSTATIC char buf[MAXPLAYERNAME+2]; + XBOXSTATIC char buf[MAXPLAYERNAME+3]; char *p; - + p = buf; // normal player colors @@ -1268,7 +1282,8 @@ static void SendNameAndColor(void) if (!strcmp(cv_playername.string, player_names[consoleplayer]) && cv_playercolor.value == players[consoleplayer].skincolor - && !strcmp(cv_skin.string, skins[players[consoleplayer].skin].name)) + && !strcmp(cv_skin.string, skins[players[consoleplayer].skin].name) + && cv_follower.value == players[consoleplayer].followerskin) return; // We'll handle it later if we're not playing. @@ -1351,6 +1366,7 @@ static void SendNameAndColor(void) WRITESTRINGN(p, cv_playername.zstring, MAXPLAYERNAME); WRITEUINT8(p, (UINT8)cv_playercolor.value); WRITEUINT8(p, (UINT8)cv_skin.value); + WRITESINT8(p, (UINT8)cv_follower.value); SendNetXCmd(XD_NAMEANDCOLOR, buf, p - buf); } @@ -1358,7 +1374,7 @@ static void SendNameAndColor(void) static void SendNameAndColor2(void) { INT32 secondplaya = -1; - XBOXSTATIC char buf[MAXPLAYERNAME+2]; + XBOXSTATIC char buf[MAXPLAYERNAME+3]; char *p; if (splitscreen < 1 && !botingame) @@ -1475,13 +1491,14 @@ static void SendNameAndColor2(void) WRITESTRINGN(p, cv_playername2.zstring, MAXPLAYERNAME); WRITEUINT8(p, (UINT8)cv_playercolor2.value); WRITEUINT8(p, (UINT8)cv_skin2.value); + WRITESINT8(p, (UINT8)cv_follower2.value); SendNetXCmd2(XD_NAMEANDCOLOR, buf, p - buf); } static void SendNameAndColor3(void) { INT32 thirdplaya = -1; - XBOXSTATIC char buf[MAXPLAYERNAME+2]; + XBOXSTATIC char buf[MAXPLAYERNAME+3]; char *p; if (splitscreen < 2) @@ -1590,13 +1607,14 @@ static void SendNameAndColor3(void) WRITESTRINGN(p, cv_playername3.zstring, MAXPLAYERNAME); WRITEUINT8(p, (UINT8)cv_playercolor3.value); WRITEUINT8(p, (UINT8)cv_skin3.value); + WRITESINT8(p, (UINT8)cv_follower3.value); SendNetXCmd3(XD_NAMEANDCOLOR, buf, p - buf); } static void SendNameAndColor4(void) { INT32 fourthplaya = -1; - XBOXSTATIC char buf[MAXPLAYERNAME+2]; + XBOXSTATIC char buf[MAXPLAYERNAME+3]; char *p; if (splitscreen < 3) @@ -1713,6 +1731,7 @@ static void SendNameAndColor4(void) WRITESTRINGN(p, cv_playername4.zstring, MAXPLAYERNAME); WRITEUINT8(p, (UINT8)cv_playercolor4.value); WRITEUINT8(p, (UINT8)cv_skin4.value); + WRITESINT8(p, (UINT8)cv_follower4.value); SendNetXCmd4(XD_NAMEANDCOLOR, buf, p - buf); } @@ -1721,7 +1740,8 @@ static void Got_NameAndColor(UINT8 **cp, INT32 playernum) player_t *p = &players[playernum]; char name[MAXPLAYERNAME+1]; UINT8 color, skin; - + SINT8 follower; + #ifdef PARANOIA if (playernum < 0 || playernum > MAXPLAYERS) I_Error("There is no player %d!", playernum); @@ -1744,6 +1764,7 @@ static void Got_NameAndColor(UINT8 **cp, INT32 playernum) READSTRINGN(*cp, name, MAXPLAYERNAME); color = READUINT8(*cp); skin = READUINT8(*cp); + follower = READSINT8(*cp); // set name if (strcasecmp(player_names[playernum], name) != 0) @@ -1802,6 +1823,9 @@ static void Got_NameAndColor(UINT8 **cp, INT32 playernum) } else SetPlayerSkinByNum(playernum, skin); + + // set follower: + SetFollower(playernum, follower); } void SendWeaponPref(void) @@ -5090,6 +5114,97 @@ static void Name4_OnChange(void) SendNameAndColor4(); } +// sends the follower change for players +static void Follower_OnChange(void) +{ + if (!Playing()) + return; // do whatever you want + + // there is a slight chance that we will actually use a string instead so... + // let's investigate the string... + char str[SKINNAMESIZE+1], cpy[SKINNAMESIZE+1]; + strcpy(str, cv_follower.string); + strcpy(cpy, cv_follower.string); + strlwr(str); + if (!atoi(cpy)) // yep, that's a string alright... + { + INT32 num = R_FollowerAvailable(str); + if (num == -1) // that's an error. + CONS_Alert(CONS_WARNING, M_GetText("Follower '%s' not found\n"), str); + + char set[10]; + sprintf(set, "%d", num); + CV_StealthSet(&cv_follower, set); // set it to a number. It's easier for us to send later :) + } + SendNameAndColor(); +} + +static void Follower2_OnChange(void) +{ + if (!Playing() || !splitscreen) + return; // do whatever you want + + char str[SKINNAMESIZE+1], cpy[SKINNAMESIZE+1]; + strcpy(str, cv_follower2.string); + strcpy(cpy, cv_follower2.string); + strlwr(str); + if (!atoi(cpy)) // yep, that's a string alright... + { + INT32 num = R_FollowerAvailable(str); + if (num == -1) // that's an error. + CONS_Alert(CONS_WARNING, M_GetText("Follower '%s' not found\n"), str); + + char set[10]; + sprintf(set, "%d", num); + CV_StealthSet(&cv_follower2, set); // set it to a number. It's easier for us to send later :) + } + SendNameAndColor2(); +} + +static void Follower3_OnChange(void) +{ + if (!Playing() || !splitscreen) + return; // do whatever you want + + char str[SKINNAMESIZE+1], cpy[SKINNAMESIZE+1]; + strcpy(str, cv_follower3.string); + strcpy(cpy, cv_follower3.string); + strlwr(str); + if (!atoi(cpy)) // yep, that's a string alright... + { + INT32 num = R_FollowerAvailable(str); + if (num == -1) // that's an error. + CONS_Alert(CONS_WARNING, M_GetText("Follower '%s' not found\n"), str); + + char set[10]; + sprintf(set, "%d", num); + CV_StealthSet(&cv_follower3, set); // set it to a number. It's easier for us to send later :) + } + SendNameAndColor3(); +} + +static void Follower4_OnChange(void) +{ + if (!Playing() || !splitscreen) + return; // do whatever you want + + char str[SKINNAMESIZE+1], cpy[SKINNAMESIZE+1]; + strcpy(str, cv_follower4.string); + strcpy(cpy, cv_follower4.string); + strlwr(str); + if (!atoi(cpy)) // yep, that's a string alright... + { + INT32 num = R_FollowerAvailable(str); + if (num == -1) // that's an error. + CONS_Alert(CONS_WARNING, M_GetText("Follower '%s' not found\n"), str); + + char set[10]; + sprintf(set, "%d", num); + CV_StealthSet(&cv_follower4, set); // set it to a number. It's easier for us to send later :) + } + SendNameAndColor4(); +} + /** Sends a skin change for the console player, unless that player is moving. * \sa cv_skin, Skin2_OnChange, Color_OnChange * \author Graue diff --git a/src/d_netcmd.h b/src/d_netcmd.h index c590eee6..0ffc65f2 100644 --- a/src/d_netcmd.h +++ b/src/d_netcmd.h @@ -21,18 +21,22 @@ extern consvar_t cv_playername; extern consvar_t cv_playercolor; extern consvar_t cv_skin; +extern consvar_t cv_follower; // secondary splitscreen player extern consvar_t cv_playername2; extern consvar_t cv_playercolor2; extern consvar_t cv_skin2; +extern consvar_t cv_follower2; // third splitscreen player extern consvar_t cv_playername3; extern consvar_t cv_playercolor3; extern consvar_t cv_skin3; +extern consvar_t cv_follower3; // fourth splitscreen player extern consvar_t cv_playername4; extern consvar_t cv_playercolor4; extern consvar_t cv_skin4; +extern consvar_t cv_follower4; // preferred number of players extern consvar_t cv_splitplayers; diff --git a/src/d_player.h b/src/d_player.h index bf1a01da..eed59d28 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -445,6 +445,7 @@ typedef struct player_s UINT8 kartspeed; // Kart speed stat between 1 and 9 UINT8 kartweight; // Kart weight stat between 1 and 9 + INT32 followerskin; // Kart: This player's follower "skin" mobj_t *follower; // Kart: This is the follower object we have. (If any) // diff --git a/src/dehacked.c b/src/dehacked.c index 7c789727..4531769a 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -699,18 +699,21 @@ INT32 numfollowers = 0; static void readfollower(MYFILE *f) { - + if (numfollowers > MAXSKINS) - { - CONS_Printf("Error: Too many followers, cannot add anymore.\n"); + { + deh_warning("Error: Too many followers, cannot add anymore.\n"); return; - } - + } + char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL); - char *word, *word2; + char *word, *word2, *dname = malloc(SKINNAMESIZE+1); char *tmp; - - CONS_Printf("Adding follower, please bear with me...\n"); + + boolean nameset; + INT32 fallbackstate = 0; + + CONS_Printf("Adding follower...\n"); do { @@ -732,14 +735,20 @@ static void readfollower(MYFILE *f) break; word2 = strtok(NULL, " = "); - if (word2) - strupr(word2); - else + + if (!word2) break; + if (word2[strlen(word2)-1] == '\n') word2[strlen(word2)-1] = '\0'; - if (fastcmp(word, "ATANGLE")) + if (fastcmp(word, "NAME")) + { + DEH_WriteUndoline(word, va("%s", followers[numfollowers].name), UNDO_NONE); + strcpy(followers[numfollowers].name, word2); + nameset = true; + } + else if (fastcmp(word, "ATANGLE")) { DEH_WriteUndoline(word, va("%d", followers[numfollowers].atangle), UNDO_NONE); followers[numfollowers].atangle = (INT32)atoi(word2); @@ -749,15 +758,105 @@ static void readfollower(MYFILE *f) DEH_WriteUndoline(word, va("%d", followers[numfollowers].zoffs), UNDO_NONE); followers[numfollowers].zoffs = (INT32)atoi(word2); } + else if (fastcmp(word, "IDLESTATE")) + { + if (word2) + strupr(word2); + DEH_WriteUndoline(word, va("%d", followers[numfollowers].idlestate), UNDO_NONE); + followers[numfollowers].idlestate = get_number(word2); + fallbackstate = followers[numfollowers].idlestate; + } + else if (fastcmp(word, "FOLLOWSTATE")) + { + if (word2) + strupr(word2); + DEH_WriteUndoline(word, va("%d", followers[numfollowers].followstate), UNDO_NONE); + followers[numfollowers].followstate = get_number(word2); + } + else if (fastcmp(word, "HURTSTATE")) + { + if (word2) + strupr(word2); + DEH_WriteUndoline(word, va("%d", followers[numfollowers].hurtstate), UNDO_NONE); + followers[numfollowers].hurtstate = get_number(word2); + } + else if (fastcmp(word, "LOSESTATE")) + { + if (word2) + strupr(word2); + DEH_WriteUndoline(word, va("%d", followers[numfollowers].losestate), UNDO_NONE); + followers[numfollowers].losestate = get_number(word2); + } + else if (fastcmp(word, "WINSTATE")) + { + if (word2) + strupr(word2); + DEH_WriteUndoline(word, va("%d", followers[numfollowers].winstate), UNDO_NONE); + followers[numfollowers].winstate = get_number(word2); + } else deh_warning("Follower %d: unknown word '%s'", numfollowers, word); } } while (!myfeof(f)); // finish when the line is empty - - CONS_Printf("We are done adding the follower.\n"); - numfollowers++; - Z_Free(s); -} + + if (!nameset) // well this is problematic. + { + strcpy(followers[numfollowers].name, va("Follower%d", numfollowers)); // this is lazy, so what + } + + // 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... + + char testname[SKINNAMESIZE]; + strcpy(testname, followers[numfollowers].name); + + // lower testname for skin checks... + strlwr(testname); + INT32 res = R_FollowerAvailable(testname); + if (res > -1) // yikes, someone else has stolen our name already + { + deh_warning("There was already a follower with the same name. (%s)", testname); + INT32 startlen = strlen(testname); + char cpy[2]; + sprintf(cpy, "%d", numfollowers); + memcpy(&testname[startlen], cpy, 2); + // in that case, we'll be very lazy and copy numfollowers to the end of our skin name. + } + + strcpy(followers[numfollowers].skinname, testname); + + // get ready to print the name... + strcpy(dname, followers[numfollowers].skinname); + + // check for atangle and zoffs. But don't error if they aren't set, just give them logical values. + if (!followers[numfollowers].atangle && followers[numfollowers].atangle != 0) + followers[numfollowers].atangle = 300; + + if ((!followers[numfollowers].zoffs || followers[numfollowers].zoffs < 0) && followers[numfollowers].zoffs != 0) // yikes, no offset or negative offset + followers[numfollowers].zoffs = 64; + + + // also check if we forgot states :V +#define NOSTATE(field, field2) \ +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); \ +} \ + + NOSTATE(idlestate, "idlestate"); + NOSTATE(followstate, "followstate"); + NOSTATE(hurtstate, "hurtstate"); + NOSTATE(losestate, "losestate"); + NOSTATE(winstate, "winstate"); +#undef NOSTATE + + CONS_Printf("Added follower '%s'\n", dname); + numfollowers++; // add 1 follower + free(dname); // free name memory + Z_Free(s); +} static void readthing(MYFILE *f, INT32 num) { @@ -3508,6 +3607,13 @@ static void DEH_LoadDehackedFile(MYFILE *f, UINT16 wad) // This is not a major mod. continue; } + else if (fastcmp(word, "FOLLOWER")) + { + readfollower(f); // at the same time this will be our only way to ADD followers for now. Yikes. + DEH_WriteUndoline(word, "", UNDO_HEADER); + // This is not a major mod either. + continue; // continue so that we don't error. + } word2 = strtok(NULL, " "); if (fastcmp(word, "CHARACTER")) { @@ -3549,11 +3655,6 @@ static void DEH_LoadDehackedFile(MYFILE *f, UINT16 wad) DEH_WriteUndoline(word, word2, UNDO_HEADER); // This is not a major mod. } - else if (fastcmp(word, "FOLLOWER")) - { - readfollower(f); // at the same time this will be our only way to ADD followers for now. Yikes. - DEH_WriteUndoline(word, word2, UNDO_HEADER); - } else if (fastcmp(word, "THING") || fastcmp(word, "MOBJ") || fastcmp(word, "OBJECT")) { if (i == 0 && word2[0] != '0') // If word2 isn't a number diff --git a/src/doomdef.h b/src/doomdef.h index 5e7b7657..ab863c6f 100644 --- a/src/doomdef.h +++ b/src/doomdef.h @@ -140,7 +140,7 @@ extern FILE *logstream; #endif -#define DEVELOP // Disable this for release builds to remove excessive cheat commands and enable MD5 checking and stuff, all in one go. :3 +//#define DEVELOP // Disable this for release builds to remove excessive cheat commands and enable MD5 checking and stuff, all in one go. :3 #ifdef DEVELOP #define VERSION 0 // Game version #define SUBVERSION 0 // more precise version number diff --git a/src/m_menu.c b/src/m_menu.c index d8b1c2d7..359ff220 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -184,6 +184,7 @@ INT16 startmap; // Mario, NiGHTS, or just a plain old normal game? static INT16 itemOn = 1; // menu item skull is on, Hack by Tails 09-18-2002 static INT16 skullAnimCounter = 10; // skull animation counter +static tic_t followertimer = 0; // Used for smooth follower floating static UINT8 setupcontrolplayer; static INT32 (*setupcontrols)[2]; // pointer to the gamecontrols of the player being edited @@ -973,6 +974,7 @@ static menuitem_t MP_PlayerSetupMenu[] = { {IT_KEYHANDLER | IT_STRING, NULL, "Name", M_HandleSetupMultiPlayer, 0}, {IT_KEYHANDLER | IT_STRING, NULL, "Character", M_HandleSetupMultiPlayer, 16}, // Tails 01-18-2001 + {IT_KEYHANDLER | IT_STRING, NULL, "Follower", M_HandleSetupMultiPlayer, 26}, {IT_KEYHANDLER | IT_STRING, NULL, "Color", M_HandleSetupMultiPlayer, 152}, }; @@ -3139,6 +3141,8 @@ void M_Ticker(void) if (--skullAnimCounter <= 0) skullAnimCounter = 8; + followertimer++; + //added : 30-01-98 : test mode for five seconds if (vidm_testingmode > 0) { @@ -8027,9 +8031,15 @@ static void M_HandleConnectIP(INT32 choice) // ======================== // Tails 03-02-2002 +// used for skin display on player setup menu static INT32 multi_tics; static state_t *multi_state; +// used for follower display on player setup menu +static INT32 follower_tics; +static INT32 follower_frame; // used for FF_ANIMATE garbo +static state_t *follower_state; + // this is set before entering the MultiPlayer setup menu, // for either player 1 or 2 static char setupm_name[MAXPLAYERNAME+1]; @@ -8037,8 +8047,10 @@ static player_t *setupm_player; static consvar_t *setupm_cvskin; static consvar_t *setupm_cvcolor; static consvar_t *setupm_cvname; +static consvar_t *setupm_cvfollower; static INT32 setupm_fakeskin; static INT32 setupm_fakecolor; +static INT32 setupm_fakefollower; // -1 is for none, our followers start at 0 static void M_DrawSetupMultiPlayerMenu(void) { @@ -8087,11 +8099,31 @@ static void M_DrawSetupMultiPlayerMenu(void) '\x1D' | highlightflags, false); // right arrow } + // draw follower string + char *fname = malloc(SKINNAMESIZE+1); + + if (setupm_fakefollower == -1) + strcpy(fname, "None"); + else + strcpy(fname, followers[setupm_fakefollower].name); + + st = V_StringWidth(fname, 0); + V_DrawString(BASEVIDWIDTH - mx - st, my + 26, + ((MP_PlayerSetupMenu[2].status & IT_TYPE) == IT_SPACE ? V_TRANSLUCENT : 0)|highlightflags|V_ALLOWLOWERCASE, + fname); + if (itemOn == 2) + { + V_DrawCharacter(BASEVIDWIDTH - mx - 10 - st - (skullAnimCounter/5), my + 26, + '\x1C' | highlightflags, false); // left arrow + V_DrawCharacter(BASEVIDWIDTH - mx + 2 + (skullAnimCounter/5), my + 26, + '\x1D' | highlightflags, false); // right arrow + } + // draw the name of the color you have chosen // Just so people don't go thinking that "Default" is Green. st = V_StringWidth(KartColor_Names[setupm_fakecolor], 0); V_DrawString(BASEVIDWIDTH - mx - st, my + 152, highlightflags|V_ALLOWLOWERCASE, KartColor_Names[setupm_fakecolor]); // SRB2kart - if (itemOn == 2) + if (itemOn == 3) { V_DrawCharacter(BASEVIDWIDTH - mx - 10 - st - (skullAnimCounter/5), my + 152, '\x1C' | highlightflags, false); // left arrow @@ -8262,9 +8294,84 @@ static void M_DrawSetupMultiPlayerMenu(void) Z_Free(colormap); } + + // draw their follower if there is one + if (setupm_fakefollower > -1 && setupm_fakefollower < numfollowers) + { + // animate the follower + + 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; + if (follower_frame > (follower_state->frame & FF_FRAMEMASK) + follower_state->var1) // that's how it works, right? + follower_frame = follower_state->frame & FF_FRAMEMASK; + } + else + { + 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? + // get spritedef: + follower_frame = follower_state->frame & FF_FRAMEMASK; + } + } + sprdef = &sprites[follower_state->sprite]; + + // draw the follower + + if (follower_frame >= sprdef->numframes) + follower_frame = 0; // frame doesn't exist, we went beyond it... what? + sprframe = &sprdef->spriteframes[follower_frame]; + patch = W_CachePatchNum(sprframe->lumppat[1], PU_CACHE); + if (sprframe->flip & 1) // Only for first sprite + flags |= V_FLIP; // This sprite is left/right flipped! + + // draw player sprite + if (setupm_fakecolor) // inverse should never happen + { + + // smooth floating, totally not stolen from rocket sneakers. + const fixed_t pi = (22<>ANGLETOFINESHIFT) & FINEMASK); + + UINT8 *colormap = R_GetTranslationColormap(-1, setupm_fakecolor, 0); + V_DrawMappedPatch(mx+65, my+90+(sine/FRACUNIT), flags, patch, colormap); + Z_Free(colormap); + } + } + #undef charw } +// follower state update. This is its own function so that it's at least somewhat clean +static void M_GetFollowerState(void) +{ + + if (setupm_fakefollower == -1 || setupm_fakefollower > numfollowers-1) // yikes, there's none! + return; + // ^ we don't actually need to set anything since it won't be displayed anyway. + + //followertimer = 0; // reset timer. not like it'll overflow anytime soon but whatever. + + // set follower state + follower_state = &states[followers[setupm_fakefollower].followstate]; + + if (follower_state->frame & FF_ANIMATE) + follower_tics = follower_state->var2; // support for FF_ANIMATE + else + follower_tics = follower_state->tics; + + follower_frame = follower_state->frame & FF_FRAMEMASK; +} + // Handle 1P/2P MP Setup static void M_HandleSetupMultiPlayer(INT32 choice) { @@ -8289,7 +8396,13 @@ static void M_HandleSetupMultiPlayer(INT32 choice) S_StartSound(NULL,sfx_menu1); // Tails setupm_fakeskin--; } - else if (itemOn == 2) // player color + else if (itemOn == 2) // follower + { + S_StartSound(NULL,sfx_menu1); + setupm_fakefollower--; + M_GetFollowerState(); // update follower state + } + else if (itemOn == 3) // player color { S_StartSound(NULL,sfx_menu1); // Tails setupm_fakecolor--; @@ -8301,8 +8414,15 @@ static void M_HandleSetupMultiPlayer(INT32 choice) { S_StartSound(NULL,sfx_menu1); // Tails setupm_fakeskin++; + M_GetFollowerState(); // update follower state } - else if (itemOn == 2) // player color + else if (itemOn == 2) // follower + { + S_StartSound(NULL,sfx_menu1); + setupm_fakefollower++; + M_GetFollowerState(); + } + else if (itemOn == 3) // player color { S_StartSound(NULL,sfx_menu1); // Tails setupm_fakecolor++; @@ -8322,7 +8442,12 @@ static void M_HandleSetupMultiPlayer(INT32 choice) setupm_name[l-1] =0; } } - else if (itemOn == 2) + else if (itemOn == 2) // follower + { + S_StartSound(NULL,sfx_menu1); + setupm_fakefollower = -1; + } + else if (itemOn == 3) { UINT8 col = skins[setupm_fakeskin].prefcolor; if (setupm_fakecolor != col) @@ -8360,6 +8485,18 @@ static void M_HandleSetupMultiPlayer(INT32 choice) if (setupm_fakeskin > numskins-1) setupm_fakeskin = 0; + // check followers: + if (setupm_fakefollower < -1) + { + setupm_fakefollower = numfollowers-1; + M_GetFollowerState(); // update follower state + } + if (setupm_fakefollower > numfollowers-1) + { + setupm_fakefollower = -1; + M_GetFollowerState(); // update follower state + } + // check color if (setupm_fakecolor < 1) setupm_fakecolor = MAXSKINCOLORS-1; @@ -8382,6 +8519,7 @@ static void M_SetupMultiPlayer(INT32 choice) multi_state = &states[mobjinfo[MT_PLAYER].seestate]; multi_tics = multi_state->tics; + strcpy(setupm_name, cv_playername.string); // set for player 1 @@ -8389,6 +8527,10 @@ static void M_SetupMultiPlayer(INT32 choice) setupm_cvskin = &cv_skin; setupm_cvcolor = &cv_playercolor; setupm_cvname = &cv_playername; + setupm_cvfollower = &cv_follower; + + setupm_fakefollower = atoi(setupm_cvfollower->string); // update fake follower value + M_GetFollowerState(); // update follower state // For whatever reason this doesn't work right if you just use ->value setupm_fakeskin = R_SkinAvailable(setupm_cvskin->string); @@ -8420,6 +8562,10 @@ static void M_SetupMultiPlayer2(INT32 choice) setupm_cvskin = &cv_skin2; setupm_cvcolor = &cv_playercolor2; setupm_cvname = &cv_playername2; + setupm_cvfollower = &cv_follower; + + setupm_fakefollower = atoi(setupm_cvfollower->string); // update fake follower value + M_GetFollowerState(); // update follower state // For whatever reason this doesn't work right if you just use ->value setupm_fakeskin = R_SkinAvailable(setupm_cvskin->string); @@ -8451,6 +8597,10 @@ static void M_SetupMultiPlayer3(INT32 choice) setupm_cvskin = &cv_skin3; setupm_cvcolor = &cv_playercolor3; setupm_cvname = &cv_playername3; + setupm_cvfollower = &cv_follower; + + setupm_fakefollower = atoi(setupm_cvfollower->string); // update fake follower value + M_GetFollowerState(); // update follower state // For whatever reason this doesn't work right if you just use ->value setupm_fakeskin = R_SkinAvailable(setupm_cvskin->string); @@ -8482,6 +8632,10 @@ static void M_SetupMultiPlayer4(INT32 choice) setupm_cvskin = &cv_skin4; setupm_cvcolor = &cv_playercolor4; setupm_cvname = &cv_playername4; + setupm_cvfollower = &cv_follower; + + setupm_fakefollower = atoi(setupm_cvfollower->string); // update fake follower value + M_GetFollowerState(); // update follower state // For whatever reason this doesn't work right if you just use ->value setupm_fakeskin = R_SkinAvailable(setupm_cvskin->string); @@ -8514,6 +8668,7 @@ static boolean M_QuitMultiPlayerMenu(void) // you know what? always putting these in the buffer won't hurt anything. COM_BufAddText (va("%s \"%s\"\n",setupm_cvskin->name,skins[setupm_fakeskin].name)); COM_BufAddText (va("%s %d\n",setupm_cvcolor->name,setupm_fakecolor)); + COM_BufAddText (va("%s %d\n",setupm_cvfollower->name,setupm_fakefollower)); return true; } diff --git a/src/r_things.c b/src/r_things.c index 198bcbbf..65041c6f 100644 --- a/src/r_things.c +++ b/src/r_things.c @@ -2637,6 +2637,19 @@ INT32 R_SkinAvailable(const char *name) return -1; } +// same thing but for followers: +INT32 R_FollowerAvailable(const char *name) +{ + INT32 i; + + for (i = 0; i < numfollowers; i++) + { + if (stricmp(followers[i].skinname,name)==0) + return i; + } + return -1; +} + // network code calls this when a 'skin change' is received boolean SetPlayerSkin(INT32 playernum, const char *skinname) { @@ -2662,13 +2675,38 @@ boolean SetPlayerSkin(INT32 playernum, const char *skinname) return false; } +// Again, same thing but for followers; +boolean SetPlayerFollower(INT32 playernum, const char *skinname) +{ + INT32 i; + player_t *player = &players[playernum]; + + for (i = 0; i < numfollowers; i++) + { + // search in the skin list + if (stricmp(followers[i].skinname, skinname) == 0) + { + SetFollower(playernum, i); + return true; + } + } + + if (P_IsLocalPlayer(player)) + CONS_Alert(CONS_WARNING, M_GetText("Follower '%s' not found.\n"), skinname); + else if(server || IsPlayerAdmin(consoleplayer)) + CONS_Alert(CONS_WARNING, M_GetText("Player %d (%s) follower '%s' not found\n"), playernum, player_names[playernum], skinname); + + SetFollower(playernum, -1); // reminder that -1 is nothing + return false; +} + // Same as SetPlayerSkin, but uses the skin #. // network code calls this when a 'skin change' is received void SetPlayerSkinByNum(INT32 playernum, INT32 skinnum) { player_t *player = &players[playernum]; skin_t *skin = &skins[skinnum]; - + CONS_Printf("skin\n"); if (skinnum >= 0 && skinnum < numskins) // Make sure it exists! { player->skin = skinnum; @@ -2727,6 +2765,30 @@ void SetPlayerSkinByNum(INT32 playernum, INT32 skinnum) SetPlayerSkinByNum(playernum, 0); // not found put the sonic skin } +// you get the drill, now we do the same for followers: +void SetFollower(INT32 playernum, INT32 skinnum) +{ + player_t *player = &players[playernum]; + + if (skinnum >= -1 && skinnum <= numfollowers) // Make sure it exists! + { + player->followerskin = skinnum; + CONS_Printf("Updated player follower num\n"); + /* + We don't actually set anything there, becasuse we need to be sure that a proper player->mo is available to spawn the follower. + Moreover, the follower will self-handle itself the rest of the time, hence, its skinnum stored to the player is all we need right now. + */ + + return; + } + + if (P_IsLocalPlayer(player)) + CONS_Alert(CONS_WARNING, M_GetText("Follower %d not found\n"), skinnum); + else if(server || IsPlayerAdmin(consoleplayer)) + CONS_Alert(CONS_WARNING, "Player %d (%s) follower %d not found\n", playernum, player_names[playernum], skinnum); + SetFollower(playernum, -1); // Not found, then set -1 (nothing) as our follower. +} + // // Add skins from a pwad, each skin preceded by 'S_SKIN' marker // diff --git a/src/r_things.h b/src/r_things.h index dda1ea16..57ecb0dd 100644 --- a/src/r_things.h +++ b/src/r_things.h @@ -122,21 +122,22 @@ typedef struct // 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 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. + // some position shenanigans: INT32 atangle; // angle the object will be at around the player. The object itself will always face the same direction as the player. INT32 zoffs; // Z offset relative to the player's height. Cannot be negative. - + // from there on out, everything is STATES to allow customization // these are only set once when the action is performed and are then free to animate however they want. - + INT32 idlestate; // state when the player is at a standstill INT32 followstate; // state when the player is moving INT32 hurtstate; // state when the player is being hurt INT32 winstate; // state when the player has won INT32 losestate; // state when the player has lost - + } follower_t; // ----------- @@ -225,6 +226,10 @@ void SetPlayerSkinByNum(INT32 playernum,INT32 skinnum); // Tails 03-16-2002 INT32 R_SkinAvailable(const char *name); void R_AddSkins(UINT16 wadnum); +INT32 R_FollowerAvailable(const char *name); +boolean SetPlayerFollower(INT32 playernum,const char *skinname); +void SetFollower(INT32 playernum,INT32 skinnum); + #ifdef DELFILE void R_DelSkins(UINT16 wadnum); #endif