From 335a86377e6fecfc346205de032550479921cdf7 Mon Sep 17 00:00:00 2001 From: Latapostrophe Date: Tue, 12 Feb 2019 18:22:58 +0100 Subject: [PATCH 01/44] A start on followers --- src/d_player.h | 3 +++ src/dehacked.c | 71 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/doomdef.h | 2 +- src/r_things.c | 3 +++ src/r_things.h | 26 ++++++++++++++++++ 5 files changed, 104 insertions(+), 1 deletion(-) diff --git a/src/d_player.h b/src/d_player.h index 27fdef8dc..e35a79723 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -442,6 +442,9 @@ typedef struct player_s // SRB2kart UINT8 kartspeed; // Kart speed stat between 1 and 9 UINT8 kartweight; // Kart weight stat between 1 and 9 + + mobj_t *follower; // Kart: This is the follower object we have. (If any) + // fixed_t normalspeed; // Normal ground diff --git a/src/dehacked.c b/src/dehacked.c index b9e29bc47..2101e8e04 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -33,6 +33,7 @@ #include "lua_script.h" #include "lua_hook.h" #include "d_clisrv.h" +#include "r_things.h" // for followers #include "m_cond.h" @@ -684,6 +685,71 @@ static void readfreeslots(MYFILE *f) Z_Free(s); } +// This here is our current only way to make followers. +INT32 numfollowers = 0; + +static void readfollower(MYFILE *f) +{ + + if (numfollowers > MAXSKINS) + { + CONS_Printf("Error: Too many followers, cannot add anymore.\n"); + return; + } + + char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL); + char *word, *word2; + char *tmp; + + CONS_Printf("Adding follower, please bear with me...\n"); + + 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) + strupr(word2); + else + break; + if (word2[strlen(word2)-1] == '\n') + word2[strlen(word2)-1] = '\0'; + + if (fastcmp(word, "ATANGLE")) + { + DEH_WriteUndoline(word, va("%d", followers[numfollowers].atangle), UNDO_NONE); + followers[numfollowers].atangle = (INT32)atoi(word2); + } + else if (fastcmp(word, "ZOFFSET") || (fastcmp(word, "ZOFFS"))) + { + DEH_WriteUndoline(word, va("%d", followers[numfollowers].zoffs), UNDO_NONE); + followers[numfollowers].zoffs = (INT32)atoi(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); +} + static void readthing(MYFILE *f, INT32 num) { char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL); @@ -3469,6 +3535,11 @@ static void DEH_LoadDehackedFile(MYFILE *f, UINT16 wad) readpatch(f, word2, wad); DEH_WriteUndoline(word, word2, UNDO_HEADER); } + 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 70e521b15..9555787c3 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/r_things.c b/src/r_things.c index d6234d4b3..7607bec1c 100644 --- a/src/r_things.c +++ b/src/r_things.c @@ -2499,7 +2499,10 @@ void R_DrawMasked(void) // // ========================================================================== +// We can assume those are tied to skins somewhat, hence why they're defined here. INT32 numskins = 0; +follower_t followers[MAXSKINS]; + skin_t skins[MAXSKINS]; // FIXTHIS: don't work because it must be inistilised before the config load //#define SKINVALUES diff --git a/src/r_things.h b/src/r_things.h index 53784415f..1aaac284d 100644 --- a/src/r_things.h +++ b/src/r_things.h @@ -115,6 +115,30 @@ typedef struct sfxenum_t soundsid[NUMSKINSOUNDS]; // sound # in S_sfx table } skin_t; +// +// for followers. +// +// We'll define these here because they're really just a mobj that'll follow some rules behind a player +// +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. + + // 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; + // ----------- // NOT SKINS STUFF ! // ----------- @@ -193,6 +217,8 @@ typedef struct drawnode_s extern INT32 numskins; extern skin_t skins[MAXSKINS]; +extern INT32 numfollowers; +extern follower_t followers[MAXSKINS]; // again, use the same rules as skins, no reason not to. void SetPlayerSkin(INT32 playernum,const char *skinname); void SetPlayerSkinByNum(INT32 playernum,INT32 skinnum); // Tails 03-16-2002 From 4721243ae5346aadae896b7b17a00d7c43588b19 Mon Sep 17 00:00:00 2001 From: Latapostrophe Date: Wed, 13 Feb 2019 10:06:52 +0100 Subject: [PATCH 02/44] 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 438cdcd54..c3596f056 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 c590eee65..0ffc65f2a 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 bf1a01dad..eed59d28e 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 7c789727a..4531769aa 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 5e7b7657b..ab863c6f6 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 d8b1c2d7b..359ff2207 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 198bcbbfd..65041c6fa 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 dda1ea16e..57ecb0dd6 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 From 92ab2845c8384384d6aff4b126da86f4620a881e Mon Sep 17 00:00:00 2001 From: Latapostrophe Date: Thu, 14 Feb 2019 03:56:28 +0100 Subject: [PATCH 03/44] Followers but they crash when you join netgames --- src/d_main.c | 1 + src/d_netcmd.c | 47 ++++++++++----- src/d_player.h | 5 +- src/dehacked.c | 41 ++++++++++--- src/doomdef.h | 2 +- src/g_game.c | 14 +++++ src/info.c | 57 +++++++++++++++++- src/info.h | 35 ++++++++++- src/m_menu.c | 20 +++++++ src/p_saveg.c | 29 +++++++-- src/p_user.c | 159 +++++++++++++++++++++++++++++++++++++++++++++++++ src/r_things.c | 17 ++++-- src/r_things.h | 1 + 13 files changed, 390 insertions(+), 38 deletions(-) diff --git a/src/d_main.c b/src/d_main.c index 5cf95f4b8..0f734f1b9 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -931,6 +931,7 @@ static void IdentifyVersion(void) D_AddFile(va(pandf,srb2waddir,"textures.kart")); D_AddFile(va(pandf,srb2waddir,"chars.kart")); D_AddFile(va(pandf,srb2waddir,"maps.kart")); + D_AddFile(va(pandf,srb2waddir,"followers.kart")); // merge this in GFX later, this is mostly to avoid uploading a MASSIVE patch.kart /gfx.kart for testing. -Lat' #ifdef USE_PATCH_KART D_AddFile(va(pandf,srb2waddir,"patch.kart")); #endif diff --git a/src/d_netcmd.c b/src/d_netcmd.c index c3596f056..9ebd0237b 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -1257,7 +1257,7 @@ static void SendNameAndColor(void) { XBOXSTATIC char buf[MAXPLAYERNAME+3]; char *p; - + p = buf; // normal player colors @@ -1280,6 +1280,10 @@ static void SendNameAndColor(void) CV_StealthSet(&cv_playercolor, cv_playercolor.defaultvalue); } + // 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.value > numfollowers-1 || cv_follower.value < -1) + CV_StealthSet(&cv_follower, "-1"); + if (!strcmp(cv_playername.string, player_names[consoleplayer]) && cv_playercolor.value == players[consoleplayer].skincolor && !strcmp(cv_skin.string, skins[players[consoleplayer].skin].name) @@ -1410,6 +1414,10 @@ static void SendNameAndColor2(void) CV_StealthSet(&cv_playercolor2, cv_playercolor2.defaultvalue); } + // 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_follower2.value > numfollowers-1 || cv_follower2.value < -1) + CV_StealthSet(&cv_follower2, "-1"); + // We'll handle it later if we're not playing. if (!Playing()) return; @@ -1534,6 +1542,10 @@ static void SendNameAndColor3(void) CV_StealthSet(&cv_playercolor3, cv_playercolor3.defaultvalue); } + // 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_follower3.value > numfollowers-1 || cv_follower3.value < -1) + CV_StealthSet(&cv_follower3, "-1"); + // We'll handle it later if we're not playing. if (!Playing()) return; @@ -1650,6 +1662,10 @@ static void SendNameAndColor4(void) CV_StealthSet(&cv_playercolor4, cv_playercolor4.defaultvalue); } + // 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_follower4.value > numfollowers-1 || cv_follower4.value < -1) + CV_StealthSet(&cv_follower4, "-1"); + // We'll handle it later if we're not playing. if (!Playing()) return; @@ -1741,7 +1757,7 @@ static void Got_NameAndColor(UINT8 **cp, INT32 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); @@ -1823,7 +1839,7 @@ static void Got_NameAndColor(UINT8 **cp, INT32 playernum) } else SetPlayerSkinByNum(playernum, skin); - + // set follower: SetFollower(playernum, follower); } @@ -1885,6 +1901,7 @@ static void Got_WeaponPref(UINT8 **cp,INT32 playernum) players[playernum].pflags |= PF_FLIPCAM; if (prefs & 2) players[playernum].pflags |= PF_ANALOGMODE; + } void D_SendPlayerConfig(void) @@ -5119,19 +5136,19 @@ 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... + if (stricmp(cpy,"0") !=0 && !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 :) @@ -5143,17 +5160,17 @@ 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... + if (stricmp(cpy,"0") !=0 && !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 :) @@ -5170,16 +5187,16 @@ static void Follower3_OnChange(void) strcpy(str, cv_follower3.string); strcpy(cpy, cv_follower3.string); strlwr(str); - if (!atoi(cpy)) // yep, that's a string alright... + if (stricmp(cpy,"0") !=0 && !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(); } @@ -5192,16 +5209,16 @@ static void Follower4_OnChange(void) strcpy(str, cv_follower4.string); strcpy(cpy, cv_follower4.string); strlwr(str); - if (!atoi(cpy)) // yep, that's a string alright... + if (stricmp(cpy,"0") !=0 && !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(); } diff --git a/src/d_player.h b/src/d_player.h index eed59d28e..7d4aec646 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -444,10 +444,11 @@ typedef struct player_s // SRB2kart 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" + boolean followerready; // Kart: Used to know when we can have a follower or not. (This is set on the first NameAndColor follower update) mobj_t *follower; // Kart: This is the follower object we have. (If any) - + // fixed_t normalspeed; // Normal ground diff --git a/src/dehacked.c b/src/dehacked.c index 4531769aa..b72a1cbc5 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -707,7 +707,7 @@ static void readfollower(MYFILE *f) } char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL); - char *word, *word2, *dname = malloc(SKINNAMESIZE+1); + char *word, *word2, dname[SKINNAMESIZE+1]; char *tmp; boolean nameset; @@ -758,6 +758,11 @@ 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, "DISTANCE") || (fastcmp(word, "DIST"))) + { + DEH_WriteUndoline(word, va("%d", followers[numfollowers].dist), UNDO_NONE); + followers[numfollowers].dist = (INT32)atoi(word2); + } else if (fastcmp(word, "IDLESTATE")) { if (word2) @@ -828,12 +833,11 @@ static void readfollower(MYFILE *f) // 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].dist < 0) + followers[numfollowers].dist = 0; - if ((!followers[numfollowers].zoffs || followers[numfollowers].zoffs < 0) && followers[numfollowers].zoffs != 0) // yikes, no offset or negative offset - followers[numfollowers].zoffs = 64; + if (followers[numfollowers].zoffs < 0) + followers[numfollowers].zoffs = 0; // also check if we forgot states :V @@ -854,7 +858,6 @@ if (!followers[numfollowers].field) \ CONS_Printf("Added follower '%s'\n", dname); numfollowers++; // add 1 follower - free(dname); // free name memory Z_Free(s); } @@ -7322,6 +7325,28 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit "S_KARMAFIREWORK4", "S_KARMAFIREWORKTRAIL", + "S_GCHAOIDLE", + "S_GCHAOFLY", + "S_GCHAOSAD1", + "S_GCHAOSAD2", + "S_GCHAOSAD3", + "S_GCHAOSAD4", + "S_GCHAOHAPPY1", + "S_GCHAOHAPPY2", + "S_GCHAOHAPPY3", + "S_GCHAOHAPPY4", + + "S_CHEESEIDLE", + "S_CHEESEFLY", + "S_CHEESESAD1", + "S_CHEESESAD2", + "S_CHEESESAD3", + "S_CHEESESAD4", + "S_CHEESEHAPPY1", + "S_CHEESEHAPPY2", + "S_CHEESEHAPPY3", + "S_CHEESEHAPPY4", + #ifdef SEENAMES "S_NAMECHECK", #endif @@ -8109,6 +8134,8 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s "MT_KARMAFIREWORK", + "MT_FOLLOWER", + #ifdef SEENAMES "MT_NAMECHECK", #endif diff --git a/src/doomdef.h b/src/doomdef.h index ab863c6f6..5e7b7657b 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/g_game.c b/src/g_game.c index f0d221ff6..04d3fe20b 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -2327,6 +2327,9 @@ void G_PlayerReborn(INT32 player) // SRB2kart UINT8 kartspeed; UINT8 kartweight; + boolean followerready; + INT32 followerskin; + mobj_t *follower; // old follower, will probably be removed by the time we're dead but you never know. // fixed_t normalspeed; fixed_t runspeed; @@ -2398,6 +2401,9 @@ void G_PlayerReborn(INT32 player) // SRB2kart kartspeed = players[player].kartspeed; kartweight = players[player].kartweight; + follower = players[player].follower; + followerready = players[player].followerready; + followerskin = players[player].followerskin; // normalspeed = players[player].normalspeed; runspeed = players[player].runspeed; @@ -2531,6 +2537,14 @@ void G_PlayerReborn(INT32 player) p->kartstuff[k_wanted] = wanted; p->kartstuff[k_eggmanblame] = -1; + if (follower) + P_RemoveMobj(follower); + + p->followerready = followerready; + p->followerskin = followerskin; + p->follower = NULL; // respawn a new one with you, it looks better. + + // Don't do anything immediately p->pflags |= PF_USEDOWN; p->pflags |= PF_ATTACKDOWN; diff --git a/src/info.c b/src/info.c index 5701f1c9e..ac16a6edc 100644 --- a/src/info.c +++ b/src/info.c @@ -69,7 +69,7 @@ char sprnames[NUMSPRITES + 1][5] = "CNDL","DOCH","DUCK","GTRE","CHES","CHIM","DRGN","LZMN","PGSS","ZTCH", "MKMA","MKMP","RTCH","BOWL","BOWH","BRRL","BRRR","HRSE","TOAH","BFRT", "OFRT","RFRT","PFRT","ASPK","HBST","HBSO","HBSF","WBLZ","WBLN","FWRK", - "XMS4","XMS5","VIEW" + "XMS4","XMS5","GCHA","CHEZ","VIEW" }; // Doesn't work with g++, needs actionf_p1 (don't modify this comment) @@ -3393,7 +3393,33 @@ state_t states[NUMSTATES] = {SPR_FWRK, 2|FF_FULLBRIGHT, 2, {NULL}, 0, 0, S_KARMAFIREWORK4}, // S_KARMAFIREWORK3 {SPR_FWRK, 3|FF_FULLBRIGHT, 2, {NULL}, 0, 0, S_KARMAFIREWORK1}, // S_KARMAFIREWORK4 {SPR_FWRK, 4|FF_FULLBRIGHT, TICRATE, {NULL}, 0, 0, S_NULL}, // S_KARMAFIREWORKTRAIL - + + // followers: + + // generic chao: + {SPR_GCHA, FF_ANIMATE, -1, {NULL}, 1, 4, S_GCHAOIDLE}, //S_GCHAOIDLE + {SPR_GCHA, 2|FF_ANIMATE, -1, {NULL}, 1, 2, S_GCHAOFLY}, //S_GCHAOFLY + {SPR_GCHA, 7, 5, {NULL}, 0, 0, S_GCHAOSAD2}, //S_GCHAOSAD1 + {SPR_GCHA, 8, 3, {NULL}, 0, 0, S_GCHAOSAD3}, //S_GCHAOSAD2 + {SPR_GCHA, 9, 6, {NULL}, 0, 0, S_GCHAOSAD4}, //S_GCHAOSAD3 + {SPR_GCHA, 8, 3, {NULL}, 0, 0, S_GCHAOSAD1}, //S_GCHAOSAD4 + {SPR_GCHA, 4, 8, {NULL}, 0, 0, S_GCHAOHAPPY2}, //S_GCHAOHAPPY1 + {SPR_GCHA, 5, 4, {NULL}, 0, 0, S_GCHAOHAPPY3}, //S_GCHAOHAPPY2 + {SPR_GCHA, 6, 8, {NULL}, 0, 0, S_GCHAOHAPPY4}, //S_GCHAOHAPPY3 + {SPR_GCHA, 5, 4, {NULL}, 0, 0, S_GCHAOHAPPY1}, //S_GCHAOHAPPY4 + + // cheese: + {SPR_CHEZ, FF_ANIMATE, -1, {NULL}, 1, 4, S_CHEESEIDLE}, //S_CHEESEIDLE + {SPR_CHEZ, 2|FF_ANIMATE, -1, {NULL}, 1, 2, S_CHEESEFLY}, //S_CHEESEFLY + {SPR_CHEZ, 7, 5, {NULL}, 0, 0, S_CHEESESAD2}, //S_CHEESESAD1 + {SPR_CHEZ, 8, 3, {NULL}, 0, 0, S_CHEESESAD3}, //S_CHEESESAD2 + {SPR_CHEZ, 9, 6, {NULL}, 0, 0, S_CHEESESAD4}, //S_CHEESESAD3 + {SPR_CHEZ, 8, 3, {NULL}, 0, 0, S_CHEESESAD1}, //S_CHEESESAD4 + {SPR_CHEZ, 4, 8, {NULL}, 0, 0, S_CHEESEHAPPY2}, //S_CHEESEHAPPY1 + {SPR_CHEZ, 5, 4, {NULL}, 0, 0, S_CHEESEHAPPY3}, //S_CHEESEHAPPY2 + {SPR_CHEZ, 6, 8, {NULL}, 0, 0, S_CHEESEHAPPY4}, //S_CHEESEHAPPY3 + {SPR_CHEZ, 5, 4, {NULL}, 0, 0, S_CHEESEHAPPY1}, //S_CHEESEHAPPY4 + #ifdef SEENAMES {SPR_NULL, 0, 1, {NULL}, 0, 0, S_NULL}, // S_NAMECHECK #endif @@ -20052,6 +20078,33 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_NULL // raisestate }, + { // MT_FOLLOWER + -1, // doomednum + S_INVISIBLE, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 8<string); // update fake follower value + + // yikes, we don't want none of that... + if (setupm_fakefollower > numfollowers-1) + setupm_fakefollower = -1; + M_GetFollowerState(); // update follower state // For whatever reason this doesn't work right if you just use ->value @@ -8565,6 +8570,11 @@ static void M_SetupMultiPlayer2(INT32 choice) setupm_cvfollower = &cv_follower; setupm_fakefollower = atoi(setupm_cvfollower->string); // update fake follower value + + // yikes, we don't want none of that... + if (setupm_fakefollower > numfollowers-1) + setupm_fakefollower = -1; + M_GetFollowerState(); // update follower state // For whatever reason this doesn't work right if you just use ->value @@ -8600,6 +8610,11 @@ static void M_SetupMultiPlayer3(INT32 choice) setupm_cvfollower = &cv_follower; setupm_fakefollower = atoi(setupm_cvfollower->string); // update fake follower value + + // yikes, we don't want none of that... + if (setupm_fakefollower > numfollowers-1) + setupm_fakefollower = -1; + M_GetFollowerState(); // update follower state // For whatever reason this doesn't work right if you just use ->value @@ -8635,6 +8650,11 @@ static void M_SetupMultiPlayer4(INT32 choice) setupm_cvfollower = &cv_follower; setupm_fakefollower = atoi(setupm_cvfollower->string); // update fake follower value + + // yikes, we don't want none of that... + if (setupm_fakefollower > numfollowers-1) + setupm_fakefollower = -1; + M_GetFollowerState(); // update follower state // For whatever reason this doesn't work right if you just use ->value diff --git a/src/p_saveg.c b/src/p_saveg.c index 975a4a5d2..ae568fabf 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -52,10 +52,11 @@ typedef enum { // RFLAGPOINT = 0x01, // BFLAGPOINT = 0x02, - CAPSULE = 0x04, - AWAYVIEW = 0x08, - FIRSTAXIS = 0x10, - SECONDAXIS = 0x20, + CAPSULE = 4, + AWAYVIEW = 8, + FIRSTAXIS = 16, + SECONDAXIS = 32, + FOLLOWER = 64, } player_saveflags; // @@ -114,6 +115,8 @@ static void P_NetArchivePlayers(void) UINT16 flags; // size_t q; + CONS_Printf("SENDING NET INFO\n"); + WRITEUINT32(save_p, ARCHIVEBLOCK_PLAYERS); for (i = 0; i < MAXPLAYERS; i++) @@ -238,6 +241,9 @@ static void P_NetArchivePlayers(void) if (players[i].axis2) flags |= SECONDAXIS; + if (players[i].follower) + flags |= FOLLOWER; + WRITEINT16(save_p, players[i].lastsidehit); WRITEINT16(save_p, players[i].lastlinehit); @@ -275,6 +281,13 @@ static void P_NetArchivePlayers(void) // SRB2kart WRITEUINT8(save_p, players[i].kartspeed); WRITEUINT8(save_p, players[i].kartweight); + + WRITEUINT8(save_p, players[i].followerskin); + WRITEUINT8(save_p, players[i].followerready); // booleans are really just numbers eh?? + if (flags & FOLLOWER) + WRITEUINT32(save_p, players[i].follower->mobjnum); + + // WRITEFIXED(save_p, players[i].normalspeed); WRITEFIXED(save_p, players[i].runspeed); @@ -299,6 +312,8 @@ static void P_NetUnArchivePlayers(void) INT32 i, j; UINT16 flags; + CONS_Printf("FETCHING NET INFO\n"); + if (READUINT32(save_p) != ARCHIVEBLOCK_PLAYERS) I_Error("Bad $$$.sav at archive block Players"); @@ -455,6 +470,12 @@ static void P_NetUnArchivePlayers(void) // SRB2kart players[i].kartspeed = READUINT8(save_p); players[i].kartweight = READUINT8(save_p); + + players[i].followerskin = READUINT8(save_p); + players[i].followerready = READUINT8(save_p); + if (flags & FOLLOWER) + players[i].follower = (mobj_t *)(size_t)READUINT32(save_p); + // players[i].normalspeed = READFIXED(save_p); players[i].runspeed = READFIXED(save_p); diff --git a/src/p_user.c b/src/p_user.c index 76f57a9ba..c2e75916b 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -8933,6 +8933,162 @@ void P_DoTimeOver(player_t *player) countdown2 = 5*TICRATE; } +/* set follower state with our weird hacks + the reason we do this is to avoid followers ever using actions (majormods, yikes!) + without having to touch p_mobj.c. + so we give it 1more tic and change the state when tic == 1 instead of 0 + cool beans? + cool beans. +*/ +static void P_SetFollowerState(mobj_t *f, INT32 state) +{ + P_SetMobjStateNF(f, state); + if (f->state->tics > 0) + f->tics++; +} + +// +//P_HandleFllower +// +//Handle the follower's spawning and moving along with the player. Do note that some of the stuff like the removal if a player doesn't exist anymore is handled in MT_FOLLOWER's thinker. +static void P_HandleFollower(player_t *player) +{ + + if (!player->followerready) + return; // we aren't ready to perform anything follower related yet. + + // How about making sure our follower exists and is added before trying to spawn it n' all? + if (player->followerskin > numfollowers-1 || player->followerskin < -1) + { + 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) + return; + + // Before we do anything, let's be sure of where we're supposed to be + follower_t fl = followers[player->followerskin]; + + angle_t an = player->mo->angle + (fl.atangle)*ANG1; // it's aproximative but it really doesn't matter in the grand scheme of things... + fixed_t zoffs = (fl.zoffs)*FRACUNIT; + + // do you like angle maths? I certainly don't... + fixed_t sx, sy, sz; + sx = player->mo->x + FixedMul((player->mo->scale*fl.dist), FINECOSINE((an)>>ANGLETOFINESHIFT)); + sy = player->mo->y + FixedMul((player->mo->scale*fl.dist), FINESINE((an)>>ANGLETOFINESHIFT)); + + // for the z coordinate, don't be a doof like Steel and forget that MFE_VERTICALFLIP exists :P + sz = player->mo->z + FixedMul(player->mo->scale, zoffs); + /*if (player->mo->eflags & MFE_VERTICALFLIP) // it's safe to assume that VERTICALFLIP accounts for MF2_OBJECTFLIP too + sz -= (player->mo->height + FixedMul(player->mo->scale, zoffs*2));*/ + // ^ handled by K_matchgenericextraflags oops + + + // finally, add a cool floating effect to the z height, unless we have no zoffs, in which case I guess this means we WANT to be on the ground...??? + if (fl.zoffs) + { + // not stolen from k_kart I swear!! + const fixed_t pi = (22<>ANGLETOFINESHIFT) & FINEMASK); + sz += sine; + } + + if (!player->follower) // follower doesn't exist / isn't valid + { + CONS_Printf("Spawning follower...\n"); + // so let's spawn one! + player->follower = P_SpawnMobj(sx, sy, sz, MT_FOLLOWER); + P_SetFollowerState(player->follower, fl.idlestate); + P_SetTarget(&player->follower->target, player->mo); // we need that to know when we need to disappear + + player->follower->extravalue1 = 0; // extravalue1 is used to know what "state set" to use. + /* + 0 = idle + 1 = forwards + 2 = hurt + 3 = win + 4 = lose + */ + } + else // follower exists, woo! + { + // first of all, handle states following the same model as above: + if (player->follower->tics == 1) + P_SetFollowerState(player->follower, player->follower->state->nextstate); + + // move the follower next to us (yes, this is really basic maths but it looks pretty damn clean in practice)! + player->follower->momx = (sx - player->follower->x)/2; + player->follower->momy = (sy - player->follower->y)/2; + player->follower->momz = (sz - player->follower->z)/6; // make z momentum a bit floatier, it'll look cute I promise! + player->follower->angle = player->mo->angle; + player->follower->color = player->mo->color; + player->follower->colorized = player->mo->colorized; + + P_SetScale(player->follower, player->mo->scale); + K_MatchGenericExtraFlags(player->follower, player->mo); + + + // handle follower animations. Yes, it looks like very bad kiddie script so what, do you have any better idea genius? Go get a life instead of criticizing my unpaid work!!!!!! + + // hurt or dead + if (player->kartstuff[k_spinouttimer] || player->mo->health <= 0) + { + player->follower->angle = leveltime*48*ANG1; + if (player->follower->extravalue1 != 2) + { + player->follower->extravalue1 = 2; + P_SetFollowerState(player->follower, fl.hurtstate); + } + if (player->mo->health <= 0) // if dead, snap to z pos + player->follower->z = sz; + } + else if (player->speed > 10*player->mo->scale) + { + if (player->follower->extravalue1 != 1) + { + player->follower->extravalue1 = 1; + P_SetFollowerState(player->follower, fl.followstate); + } + } + else // nvm you're slow + { + if (player->follower->extravalue1 != 0) + { + + if (player->exiting) + { + if (K_IsPlayerLosing(player)) // L + { + if (player->follower->extravalue1 != 4) + { + player->follower->extravalue1 = 4; + P_SetFollowerState(player->follower, fl.losestate); + } + } + else // W + { + if (player->follower->extravalue1 != 3) + { + player->follower->extravalue1 = 3; + P_SetFollowerState(player->follower, fl.winstate); + } + } + } + else + { + player->follower->extravalue1 = 0; + P_SetFollowerState(player->follower, fl.idlestate); + } + } + } + } +} + // // P_PlayerThink // @@ -9466,6 +9622,9 @@ void P_PlayerThink(player_t *player) K_KartPlayerThink(player, cmd); // SRB2kart + // we're done doing all of this, now take care of followers... + P_HandleFollower(player); + /* // Colormap verification { diff --git a/src/r_things.c b/src/r_things.c index 65041c6fa..7999db028 100644 --- a/src/r_things.c +++ b/src/r_things.c @@ -2502,6 +2502,8 @@ void R_DrawMasked(void) // We can assume those are tied to skins somewhat, hence why they're defined here. INT32 numskins = 0; follower_t followers[MAXSKINS]; +// default followers are defined in SOC_FLWR in followers.kart / gfx.kart (depending on what exe this is, at this point) + skin_t skins[MAXSKINS]; // FIXTHIS: don't work because it must be inistilised before the config load @@ -2706,7 +2708,6 @@ 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; @@ -2769,16 +2770,22 @@ void SetPlayerSkinByNum(INT32 playernum, INT32 skinnum) void SetFollower(INT32 playernum, INT32 skinnum) { player_t *player = &players[playernum]; - + + player->followerready = true; // we are ready to perform follower related actions in the player thinker, now. 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. + We don't spawn the follower here since it'll be easier to handle all of it in the Player thinker itself. + 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) + { + P_RemoveMobj(player->follower); + player->follower = NULL; + } + return; } diff --git a/src/r_things.h b/src/r_things.h index 57ecb0dd6..cfb862f0b 100644 --- a/src/r_things.h +++ b/src/r_things.h @@ -127,6 +127,7 @@ typedef struct follower_s // 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 dist; // distance relative to the player. (In a circle) INT32 zoffs; // Z offset relative to the player's height. Cannot be negative. // from there on out, everything is STATES to allow customization From 2c85e79c6af173dda0b83b44fdc73cd25ac171aa Mon Sep 17 00:00:00 2001 From: wolfy852 Date: Tue, 26 Feb 2019 17:09:24 -0600 Subject: [PATCH 04/44] Fix players crashing when joining netgames with active followers --- src/doomdef.h | 2 +- src/p_saveg.c | 7 +++++++ src/p_user.c | 2 +- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/doomdef.h b/src/doomdef.h index 5e7b7657b..ab863c6f6 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/p_saveg.c b/src/p_saveg.c index ae568fabf..546280836 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -3105,6 +3105,13 @@ static void P_RelinkPointers(void) if (!P_SetTarget(&mobj->player->awayviewmobj, P_FindNewPosition(temp))) CONS_Debug(DBG_GAMELOGIC, "awayviewmobj not found on %d\n", mobj->type); } + if (mobj->player && mobj->player->follower) + { + temp = (UINT32)(size_t)mobj->player->follower; + mobj->player->follower = NULL; + if (!P_SetTarget(&mobj->player->follower, P_FindNewPosition(temp))) + CONS_Debug(DBG_GAMELOGIC, "follower not found on %d\n", mobj->type); + } } } } diff --git a/src/p_user.c b/src/p_user.c index c2e75916b..7ee0c2d64 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -8948,7 +8948,7 @@ static void P_SetFollowerState(mobj_t *f, INT32 state) } // -//P_HandleFllower +//P_HandleFollower // //Handle the follower's spawning and moving along with the player. Do note that some of the stuff like the removal if a player doesn't exist anymore is handled in MT_FOLLOWER's thinker. static void P_HandleFollower(player_t *player) From b5b1ca6e26cf7736d7874c0600ba161cb0f8c0af Mon Sep 17 00:00:00 2001 From: Latapostrophe Date: Sat, 2 Mar 2019 22:39:42 +0100 Subject: [PATCH 05/44] Fix splitscreen, offline and death --- src/d_netcmd.c | 19 +++++++++++++++++++ src/m_menu.c | 6 +++--- src/p_user.c | 10 +++++----- 3 files changed, 27 insertions(+), 8 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index f73a53ce4..3a1c3fa2d 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -1317,6 +1317,10 @@ static void SendNameAndColor(void) if (players[consoleplayer].mo) players[consoleplayer].mo->color = players[consoleplayer].skincolor; + // Update follower for local games: + if (cv_follower.value >= 0 && cv_follower.value != players[consoleplayer].followerskin) + SetFollower(consoleplayer, cv_follower.value); + if (metalrecording) { // Metal Sonic is Sonic, obviously. SetPlayerSkinByNum(consoleplayer, 0); @@ -1453,6 +1457,10 @@ static void SendNameAndColor2(void) if (players[secondplaya].mo) players[secondplaya].mo->color = players[secondplaya].skincolor; + // Update follower for local games: + if (cv_follower2.value >= 0 && cv_follower2.value != players[secondplaya].followerskin) + SetFollower(secondplaya, cv_follower2.value); + if ((foundskin = R_SkinAvailable(cv_skin2.string)) != -1) { //boolean notsame; @@ -1573,6 +1581,10 @@ static void SendNameAndColor3(void) if (players[thirdplaya].mo) players[thirdplaya].mo->color = players[thirdplaya].skincolor; + // Update follower for local games: + if (cv_follower3.value >= 0 && cv_follower3.value != players[thirdplaya].followerskin) + SetFollower(thirdplaya, cv_follower3.value); + if ((foundskin = R_SkinAvailable(cv_skin3.string)) != -1) { //boolean notsame; @@ -1701,6 +1713,10 @@ static void SendNameAndColor4(void) if (players[fourthplaya].mo) players[fourthplaya].mo->color = players[fourthplaya].skincolor; + // Update follower for local games: + if (cv_follower4.value >= 0 && cv_follower4.value != players[fourthplaya].followerskin) + SetFollower(fourthplaya, cv_follower4.value); + if ((foundskin = R_SkinAvailable(cv_skin4.string)) != -1) { //boolean notsame; @@ -2129,6 +2145,9 @@ void D_MapChange(INT32 mapnum, INT32 newgametype, boolean pencoremode, boolean r } chmappending++; + + // send infos. This seems very dumb but we use this for offline games and followers as some kind of band aid + if (netgame) WRITEUINT32(buf_p, M_RandomizedSeed()); // random seed SendNetXCmd(XD_MAP, buf, buf_p - buf); diff --git a/src/m_menu.c b/src/m_menu.c index 2c69cc70e..0db7d12a9 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -8576,7 +8576,7 @@ static void M_SetupMultiPlayer2(INT32 choice) setupm_cvskin = &cv_skin2; setupm_cvcolor = &cv_playercolor2; setupm_cvname = &cv_playername2; - setupm_cvfollower = &cv_follower; + setupm_cvfollower = &cv_follower2; setupm_fakefollower = atoi(setupm_cvfollower->string); // update fake follower value @@ -8616,7 +8616,7 @@ static void M_SetupMultiPlayer3(INT32 choice) setupm_cvskin = &cv_skin3; setupm_cvcolor = &cv_playercolor3; setupm_cvname = &cv_playername3; - setupm_cvfollower = &cv_follower; + setupm_cvfollower = &cv_follower3; setupm_fakefollower = atoi(setupm_cvfollower->string); // update fake follower value @@ -8656,7 +8656,7 @@ static void M_SetupMultiPlayer4(INT32 choice) setupm_cvskin = &cv_skin4; setupm_cvcolor = &cv_playercolor4; setupm_cvname = &cv_playername4; - setupm_cvfollower = &cv_follower; + setupm_cvfollower = &cv_follower4; setupm_fakefollower = atoi(setupm_cvfollower->string); // update fake follower value diff --git a/src/p_user.c b/src/p_user.c index 7ee0c2d64..43dc5663e 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -9044,8 +9044,8 @@ static void P_HandleFollower(player_t *player) player->follower->extravalue1 = 2; P_SetFollowerState(player->follower, fl.hurtstate); } - if (player->mo->health <= 0) // if dead, snap to z pos - player->follower->z = sz; + if (player->mo->health <= 0) // if dead, follow the player's z momentum exactly so they both look like they die at the same speed. + player->follower->momz = player->mo->momz; } else if (player->speed > 10*player->mo->scale) { @@ -9151,6 +9151,9 @@ void P_PlayerThink(player_t *player) player->awayviewtics = 0; // reset to zero } + // Run followes here. We need them to run even when we're dead to follow through what we're doing. + P_HandleFollower(player); + /* if (player->pflags & PF_GLIDING) { @@ -9622,9 +9625,6 @@ void P_PlayerThink(player_t *player) K_KartPlayerThink(player, cmd); // SRB2kart - // we're done doing all of this, now take care of followers... - P_HandleFollower(player); - /* // Colormap verification { From c3151039b20f262f8ab1f9172f86a4e1edbbed41 Mon Sep 17 00:00:00 2001 From: Latapostrophe Date: Sat, 2 Mar 2019 22:40:58 +0100 Subject: [PATCH 06/44] leftover comment from earlier experiment --- src/d_netcmd.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 3a1c3fa2d..81f14762b 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -2146,8 +2146,6 @@ void D_MapChange(INT32 mapnum, INT32 newgametype, boolean pencoremode, boolean r chmappending++; - // send infos. This seems very dumb but we use this for offline games and followers as some kind of band aid - if (netgame) WRITEUINT32(buf_p, M_RandomizedSeed()); // random seed SendNetXCmd(XD_MAP, buf, buf_p - buf); From 980ab4dc771f2bc2ff19669fe113e00504ea2205 Mon Sep 17 00:00:00 2001 From: Alam Arias Date: Sat, 2 Mar 2019 18:48:00 -0500 Subject: [PATCH 07/44] follower_frame should be unsigned --- src/m_menu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/m_menu.c b/src/m_menu.c index 63b1bea4e..528e9c5ff 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -8046,7 +8046,7 @@ 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 UINT32 follower_frame; // used for FF_ANIMATE garbo static state_t *follower_state; // this is set before entering the MultiPlayer setup menu, From 6071b78cf3054387e6c1fb809c7d6afa2a818acb Mon Sep 17 00:00:00 2001 From: Alam Arias Date: Sat, 2 Mar 2019 18:57:52 -0500 Subject: [PATCH 08/44] fixed compiling for MSVC --- src/d_netcmd.c | 26 ++++++++++++++------------ src/dehacked.c | 20 +++++++++++--------- src/m_menu.c | 3 ++- src/p_user.c | 11 +++++++---- 4 files changed, 34 insertions(+), 26 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index ea8b0db88..0d5a93e16 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -5162,22 +5162,24 @@ static void Name4_OnChange(void) // sends the follower change for players static void Follower_OnChange(void) { + char str[SKINNAMESIZE+1], cpy[SKINNAMESIZE+1]; + 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 (stricmp(cpy,"0") !=0 && !atoi(cpy)) // yep, that's a string alright... { INT32 num = R_FollowerAvailable(str); - if (num == -1) // that's an error. + char set[10]; + + 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 :) } @@ -5186,20 +5188,20 @@ static void Follower_OnChange(void) static void Follower2_OnChange(void) { + char str[SKINNAMESIZE+1], cpy[SKINNAMESIZE+1]; 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 (stricmp(cpy,"0") !=0 && !atoi(cpy)) // yep, that's a string alright... { INT32 num = R_FollowerAvailable(str); - if (num == -1) // that's an error. + char set[10]; + 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 :) } @@ -5208,20 +5210,20 @@ static void Follower2_OnChange(void) static void Follower3_OnChange(void) { + char str[SKINNAMESIZE+1], cpy[SKINNAMESIZE+1]; 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 (stricmp(cpy,"0") !=0 && !atoi(cpy)) // yep, that's a string alright... { INT32 num = R_FollowerAvailable(str); - if (num == -1) // that's an error. + char set[10]; + 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 :) } @@ -5230,20 +5232,20 @@ static void Follower3_OnChange(void) static void Follower4_OnChange(void) { + char str[SKINNAMESIZE+1], cpy[SKINNAMESIZE+1]; 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 (stricmp(cpy,"0") !=0 && !atoi(cpy)) // yep, that's a string alright... { INT32 num = R_FollowerAvailable(str); - if (num == -1) // that's an error. + char set[10]; + 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 :) } diff --git a/src/dehacked.c b/src/dehacked.c index 2ed609e6e..3a766df30 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -699,6 +699,14 @@ INT32 numfollowers = 0; static void readfollower(MYFILE *f) { + char *s; + char *word, *word2, dname[SKINNAMESIZE+1]; + char *tmp; + char testname[SKINNAMESIZE]; + + boolean nameset; + INT32 fallbackstate = 0; + INT32 res; if (numfollowers > MAXSKINS) { @@ -706,12 +714,7 @@ static void readfollower(MYFILE *f) return; } - char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL); - char *word, *word2, dname[SKINNAMESIZE+1]; - char *tmp; - - boolean nameset; - INT32 fallbackstate = 0; + s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL); CONS_Printf("Adding follower...\n"); @@ -812,17 +815,16 @@ static 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... - char testname[SKINNAMESIZE]; strcpy(testname, followers[numfollowers].name); // lower testname for skin checks... strlwr(testname); - INT32 res = R_FollowerAvailable(testname); + 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]; + deh_warning("There was already a follower with the same name. (%s)", testname); 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. diff --git a/src/m_menu.c b/src/m_menu.c index 528e9c5ff..82df9020e 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -8077,6 +8077,7 @@ static void M_DrawSetupMultiPlayerMenu(void) UINT8 i; const UINT8 *flashcol = V_GetStringColormap(highlightflags); INT32 statx, staty; + char *fname; mx = MP_PlayerSetupDef.x; my = MP_PlayerSetupDef.y; @@ -8109,7 +8110,7 @@ static void M_DrawSetupMultiPlayerMenu(void) } // draw follower string - char *fname = malloc(SKINNAMESIZE+1); + fname = malloc(SKINNAMESIZE+1); if (setupm_fakefollower == -1) strcpy(fname, "None"); diff --git a/src/p_user.c b/src/p_user.c index bfc9d8c44..7ca368b7b 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -8940,6 +8940,10 @@ static void P_SetFollowerState(mobj_t *f, INT32 state) //Handle the follower's spawning and moving along with the player. Do note that some of the stuff like the removal if a player doesn't exist anymore is handled in MT_FOLLOWER's thinker. static void P_HandleFollower(player_t *player) { + follower_t fl; + angle_t an; + fixed_t zoffs; + fixed_t sx, sy, sz; if (!player->followerready) return; // we aren't ready to perform anything follower related yet. @@ -8959,13 +8963,12 @@ static void P_HandleFollower(player_t *player) return; // Before we do anything, let's be sure of where we're supposed to be - follower_t fl = followers[player->followerskin]; + fl = followers[player->followerskin]; - angle_t an = player->mo->angle + (fl.atangle)*ANG1; // it's aproximative but it really doesn't matter in the grand scheme of things... - fixed_t zoffs = (fl.zoffs)*FRACUNIT; + an = player->mo->angle + (fl.atangle)*ANG1; // it's aproximative but it really doesn't matter in the grand scheme of things... + zoffs = (fl.zoffs)*FRACUNIT; // do you like angle maths? I certainly don't... - fixed_t sx, sy, sz; sx = player->mo->x + FixedMul((player->mo->scale*fl.dist), FINECOSINE((an)>>ANGLETOFINESHIFT)); sy = player->mo->y + FixedMul((player->mo->scale*fl.dist), FINESINE((an)>>ANGLETOFINESHIFT)); From 2967638cf3b8b967408803aa4901ccb00563950c Mon Sep 17 00:00:00 2001 From: Alam Arias Date: Sat, 2 Mar 2019 19:49:21 -0500 Subject: [PATCH 09/44] remove whitespaces --- src/info.c | 10 +++++----- src/info.h | 14 +++++++------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/info.c b/src/info.c index ac16a6edc..0367d3faf 100644 --- a/src/info.c +++ b/src/info.c @@ -3393,9 +3393,9 @@ state_t states[NUMSTATES] = {SPR_FWRK, 2|FF_FULLBRIGHT, 2, {NULL}, 0, 0, S_KARMAFIREWORK4}, // S_KARMAFIREWORK3 {SPR_FWRK, 3|FF_FULLBRIGHT, 2, {NULL}, 0, 0, S_KARMAFIREWORK1}, // S_KARMAFIREWORK4 {SPR_FWRK, 4|FF_FULLBRIGHT, TICRATE, {NULL}, 0, 0, S_NULL}, // S_KARMAFIREWORKTRAIL - + // followers: - + // generic chao: {SPR_GCHA, FF_ANIMATE, -1, {NULL}, 1, 4, S_GCHAOIDLE}, //S_GCHAOIDLE {SPR_GCHA, 2|FF_ANIMATE, -1, {NULL}, 1, 2, S_GCHAOFLY}, //S_GCHAOFLY @@ -3407,7 +3407,7 @@ state_t states[NUMSTATES] = {SPR_GCHA, 5, 4, {NULL}, 0, 0, S_GCHAOHAPPY3}, //S_GCHAOHAPPY2 {SPR_GCHA, 6, 8, {NULL}, 0, 0, S_GCHAOHAPPY4}, //S_GCHAOHAPPY3 {SPR_GCHA, 5, 4, {NULL}, 0, 0, S_GCHAOHAPPY1}, //S_GCHAOHAPPY4 - + // cheese: {SPR_CHEZ, FF_ANIMATE, -1, {NULL}, 1, 4, S_CHEESEIDLE}, //S_CHEESEIDLE {SPR_CHEZ, 2|FF_ANIMATE, -1, {NULL}, 1, 2, S_CHEESEFLY}, //S_CHEESEFLY @@ -3419,7 +3419,7 @@ state_t states[NUMSTATES] = {SPR_CHEZ, 5, 4, {NULL}, 0, 0, S_CHEESEHAPPY3}, //S_CHEESEHAPPY2 {SPR_CHEZ, 6, 8, {NULL}, 0, 0, S_CHEESEHAPPY4}, //S_CHEESEHAPPY3 {SPR_CHEZ, 5, 4, {NULL}, 0, 0, S_CHEESEHAPPY1}, //S_CHEESEHAPPY4 - + #ifdef SEENAMES {SPR_NULL, 0, 1, {NULL}, 0, 0, S_NULL}, // S_NAMECHECK #endif @@ -20103,7 +20103,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = sfx_None, // activesound MF_NOCLIPTHING|MF_NOGRAVITY|MF_NOCLIP|MF_NOCLIPHEIGHT, // flags S_NULL // raisestate - }, + }, // ============================================================================================================================// diff --git a/src/info.h b/src/info.h index 418c8790f..b55795e66 100644 --- a/src/info.h +++ b/src/info.h @@ -779,10 +779,10 @@ typedef enum sprite // Xmas-specific sprites that don't fit aboxe SPR_XMS4, SPR_XMS5, - + SPR_GCHA, // follower: generic chao SPR_CHEZ, // follower: cheese - + // First person view sprites; this is a sprite so that it can be replaced by a specialized MD2 draw later SPR_VIEW, @@ -4054,9 +4054,9 @@ typedef enum state S_KARMAFIREWORK3, S_KARMAFIREWORK4, S_KARMAFIREWORKTRAIL, - + // followers: - + // generic chao: S_GCHAOIDLE, S_GCHAOFLY, @@ -4068,7 +4068,7 @@ typedef enum state S_GCHAOHAPPY2, S_GCHAOHAPPY3, S_GCHAOHAPPY4, - + // cheese: S_CHEESEIDLE, S_CHEESEFLY, @@ -4882,9 +4882,9 @@ typedef enum mobj_type MT_BOOSTON, MT_LIZARDMAN, MT_LIONMAN, - + MT_KARMAFIREWORK, - + MT_FOLLOWER, #ifdef SEENAMES From d8576b0bbca6389c1113a0f432a9c9a4bb0f992b Mon Sep 17 00:00:00 2001 From: Latapostrophe Date: Sun, 3 Mar 2019 21:12:21 +0100 Subject: [PATCH 10/44] Add more customization options & fix some dumb things --- src/dehacked.c | 59 +++++++++++++++++++++++++++++++++++++++++++++++--- src/m_menu.c | 6 +++-- src/p_user.c | 36 +++++++++++++++--------------- src/r_things.c | 2 +- src/r_things.h | 12 ++++++++-- 5 files changed, 89 insertions(+), 26 deletions(-) diff --git a/src/dehacked.c b/src/dehacked.c index 3a766df30..7b6fce01c 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -716,7 +716,15 @@ static void readfollower(MYFILE *f) s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL); - CONS_Printf("Adding follower...\n"); + // 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. + followers[numfollowers].scale = FRACUNIT; + followers[numfollowers].atangle = 230; + followers[numfollowers].dist = 16; + followers[numfollowers].zoffs = 32; + followers[numfollowers].horzlag = 2; + followers[numfollowers].vertlag = 4; + followers[numfollowers].bobspeed = TICRATE*2; + followers[numfollowers].bobamp = 4; do { @@ -751,11 +759,36 @@ static void readfollower(MYFILE *f) strcpy(followers[numfollowers].name, word2); nameset = true; } + else if (fastcmp(word, "SCALE")) + { + DEH_WriteUndoline(word, va("%d", followers[numfollowers].scale), UNDO_NONE); + followers[numfollowers].scale = get_number(word2); + } else if (fastcmp(word, "ATANGLE")) { DEH_WriteUndoline(word, va("%d", followers[numfollowers].atangle), UNDO_NONE); followers[numfollowers].atangle = (INT32)atoi(word2); } + else if (fastcmp(word, "HORZLAG")) + { + DEH_WriteUndoline(word, va("%d", followers[numfollowers].horzlag), UNDO_NONE); + followers[numfollowers].horzlag = (INT32)atoi(word2); + } + else if (fastcmp(word, "VERTLAG")) + { + DEH_WriteUndoline(word, va("%d", followers[numfollowers].vertlag), UNDO_NONE); + followers[numfollowers].vertlag = (INT32)atoi(word2); + } + else if (fastcmp(word, "BOBSPEED")) + { + DEH_WriteUndoline(word, va("%d", followers[numfollowers].bobspeed), UNDO_NONE); + followers[numfollowers].bobspeed = (INT32)atoi(word2); + } + else if (fastcmp(word, "BOBAMP")) + { + DEH_WriteUndoline(word, va("%d", followers[numfollowers].bobamp), UNDO_NONE); + followers[numfollowers].bobamp = (INT32)atoi(word2); + } else if (fastcmp(word, "ZOFFSET") || (fastcmp(word, "ZOFFS"))) { DEH_WriteUndoline(word, va("%d", followers[numfollowers].zoffs), UNDO_NONE); @@ -824,7 +857,7 @@ static void readfollower(MYFILE *f) { INT32 startlen = strlen(testname); char cpy[2]; - deh_warning("There was already a follower with the same name. (%s)", testname); + //deh_warning("There was already a follower with the same name. (%s)", testname); This warning probably isn't necessary anymore? 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. @@ -841,8 +874,28 @@ static void readfollower(MYFILE *f) if (followers[numfollowers].zoffs < 0) followers[numfollowers].zoffs = 0; + // HORZLAG and VERTLAG must ABSOLUTELY be higher than 0. If 0, the game crashes, if negative, weird shit happens! + if (followers[numfollowers].horzlag <= 0) + followers[numfollowers].horzlag = 1; + + if (followers[numfollowers].vertlag <= 0) + followers[numfollowers].vertlag = 1; + + // scale must be positive for obvious reasons, and so must both of the bob related variables + if (followers[numfollowers].scale <= 0) + followers[numfollowers].scale = 1; + + // Bob amplitude can totally be 0 + if (followers[numfollowers].bobamp < 0) + followers[numfollowers].bobamp = 1; + + // so can bob speed + if (followers[numfollowers].bobspeed < 0) + followers[numfollowers].bobspeed = 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. - // also check if we forgot states :V #define NOSTATE(field, field2) \ if (!followers[numfollowers].field) \ { \ diff --git a/src/m_menu.c b/src/m_menu.c index 82df9020e..17a71d2f5 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -8348,12 +8348,14 @@ static void M_DrawSetupMultiPlayerMenu(void) if (setupm_fakecolor) // 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. const fixed_t pi = (22<>ANGLETOFINESHIFT) & FINEMASK); + fixed_t sine = fl.bobamp * FINESINE((((8*pi*(fl.bobspeed)) * followertimer)>>ANGLETOFINESHIFT) & FINEMASK); UINT8 *colormap = R_GetTranslationColormap(-1, setupm_fakecolor, 0); - V_DrawMappedPatch(mx+65, my+90+(sine/FRACUNIT), flags, patch, colormap); + V_DrawFixedPatch((mx+65)*FRACUNIT, (my+131-fl.zoffs)*FRACUNIT+sine, fl.scale, flags, patch, colormap); Z_Free(colormap); } } diff --git a/src/p_user.c b/src/p_user.c index 7ca368b7b..1b8748b2e 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -8979,18 +8979,15 @@ static void P_HandleFollower(player_t *player) // ^ handled by K_matchgenericextraflags oops - // finally, add a cool floating effect to the z height, unless we have no zoffs, in which case I guess this means we WANT to be on the ground...??? - if (fl.zoffs) - { - // not stolen from k_kart I swear!! - const fixed_t pi = (22<>ANGLETOFINESHIFT) & FINEMASK); - sz += sine; - } + // finally, add a cool floating effect to the z height. + // not stolen from k_kart I swear!! + const fixed_t pi = (22<>ANGLETOFINESHIFT) & FINEMASK); + sz += sine; if (!player->follower) // follower doesn't exist / isn't valid { - CONS_Printf("Spawning follower...\n"); + //CONS_Printf("Spawning follower...\n"); // so let's spawn one! player->follower = P_SpawnMobj(sx, sy, sz, MT_FOLLOWER); P_SetFollowerState(player->follower, fl.idlestate); @@ -9012,14 +9009,14 @@ static void P_HandleFollower(player_t *player) P_SetFollowerState(player->follower, player->follower->state->nextstate); // move the follower next to us (yes, this is really basic maths but it looks pretty damn clean in practice)! - player->follower->momx = (sx - player->follower->x)/2; - player->follower->momy = (sy - player->follower->y)/2; - player->follower->momz = (sz - player->follower->z)/6; // make z momentum a bit floatier, it'll look cute I promise! + player->follower->momx = (sx - player->follower->x)/fl.horzlag; + player->follower->momy = (sy - player->follower->y)/fl.horzlag; + player->follower->momz = (sz - player->follower->z)/fl.vertlag; // make z momentum a bit floatier, it'll look cute I promise! player->follower->angle = player->mo->angle; player->follower->color = player->mo->color; player->follower->colorized = player->mo->colorized; - P_SetScale(player->follower, player->mo->scale); + P_SetScale(player->follower, FixedMul(fl.scale, player->mo->scale)); K_MatchGenericExtraFlags(player->follower, player->mo); @@ -9028,7 +9025,7 @@ static void P_HandleFollower(player_t *player) // hurt or dead if (player->kartstuff[k_spinouttimer] || player->mo->health <= 0) { - player->follower->angle = leveltime*48*ANG1; + player->follower->angle = -leveltime*48*ANG1; // spin out if (player->follower->extravalue1 != 2) { player->follower->extravalue1 = 2; @@ -9037,20 +9034,23 @@ static void P_HandleFollower(player_t *player) if (player->mo->health <= 0) // if dead, follow the player's z momentum exactly so they both look like they die at the same speed. player->follower->momz = player->mo->momz; } - else if (player->speed > 10*player->mo->scale) + else if (player->speed > 10*player->mo->scale) // animation for moving fast enough { + // if we're moving fast enough, let's make the angle the direction we're moving towards. This is to avoid drifting looking awkward. + player->follower->angle = R_PointToAngle2(0, 0, player->follower->momx, player->follower->momy); + if (player->follower->extravalue1 != 1) { player->follower->extravalue1 = 1; P_SetFollowerState(player->follower, fl.followstate); } } - else // nvm you're slow + else // animations when nearly still. This includes winning and losing. { if (player->follower->extravalue1 != 0) { - if (player->exiting) + if (player->exiting) // win/ loss animations { if (K_IsPlayerLosing(player)) // L { @@ -9069,7 +9069,7 @@ static void P_HandleFollower(player_t *player) } } } - else + else // normal standstill { player->follower->extravalue1 = 0; P_SetFollowerState(player->follower, fl.idlestate); diff --git a/src/r_things.c b/src/r_things.c index b5a7f7116..f8d84b030 100644 --- a/src/r_things.c +++ b/src/r_things.c @@ -2775,7 +2775,7 @@ void SetFollower(INT32 playernum, INT32 skinnum) if (skinnum >= -1 && skinnum <= numfollowers) // Make sure it exists! { player->followerskin = skinnum; - CONS_Printf("Updated player follower num\n"); + //CONS_Printf("Updated player follower num\n"); /* We don't spawn the follower here since it'll be easier to handle all of it in the Player thinker itself. 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. diff --git a/src/r_things.h b/src/r_things.h index cfb862f0b..ec70bef7d 100644 --- a/src/r_things.h +++ b/src/r_things.h @@ -124,11 +124,20 @@ 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. - + + fixed_t scale; // Scale relative to the player's. + // 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 dist; // distance relative to the player. (In a circle) INT32 zoffs; // Z offset relative to the player's height. Cannot be negative. + + // movement options + + INT32 horzlag; // Lag for X/Y displacement. Default is 2. Must be > 0 because we divide by this number. + INT32 vertlag; // not Vert from Neptunia lagging, this is for Z displacement lag Default is 4. Must be > 0 because we divide by this number. + INT32 bobamp; // Bob amplitude. Default is 4. + INT32 bobspeed; // Arbitrary modifier for bobbing speed, default is TICRATE*2 (70). // 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. @@ -138,7 +147,6 @@ typedef struct follower_s 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; // ----------- From d410143ab0c02dfdc6f41b02653d2a5d4666c9c6 Mon Sep 17 00:00:00 2001 From: Latapostrophe Date: Sun, 3 Mar 2019 21:16:02 +0100 Subject: [PATCH 11/44] good grief I'm an idiot --- src/r_things.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/r_things.h b/src/r_things.h index ec70bef7d..ca9215f9a 100644 --- a/src/r_things.h +++ b/src/r_things.h @@ -135,7 +135,7 @@ typedef struct follower_s // movement options INT32 horzlag; // Lag for X/Y displacement. Default is 2. Must be > 0 because we divide by this number. - INT32 vertlag; // not Vert from Neptunia lagging, this is for Z displacement lag Default is 4. Must be > 0 because we divide by this number. + INT32 vertlag; // not Vert from Neptunia lagging, this is for Z displacement lag Default is 6. Must be > 0 because we divide by this number. INT32 bobamp; // Bob amplitude. Default is 4. INT32 bobspeed; // Arbitrary modifier for bobbing speed, default is TICRATE*2 (70). From 3aadc5ac1be5dd7a7d07ee434a01b9867b2f2de4 Mon Sep 17 00:00:00 2001 From: Latapostrophe Date: Sun, 3 Mar 2019 21:16:56 +0100 Subject: [PATCH 12/44] when you change the documentation but forget to update the code to go along --- src/dehacked.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dehacked.c b/src/dehacked.c index 7b6fce01c..f34b3b58a 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -722,7 +722,7 @@ static void readfollower(MYFILE *f) followers[numfollowers].dist = 16; followers[numfollowers].zoffs = 32; followers[numfollowers].horzlag = 2; - followers[numfollowers].vertlag = 4; + followers[numfollowers].vertlag = 6; followers[numfollowers].bobspeed = TICRATE*2; followers[numfollowers].bobamp = 4; From 169ec43eecdaabd6faa1fafa9aafd922360fa1d1 Mon Sep 17 00:00:00 2001 From: Latapostrophe Date: Mon, 4 Mar 2019 21:35:58 +0100 Subject: [PATCH 13/44] add some Lua support, hit confirm animations and fix some stuff --- src/dehacked.c | 18 ++++++++++++++++++ src/k_kart.c | 7 +++++++ src/lua_playerlib.c | 8 ++++++++ src/p_user.c | 31 ++++++++++++++++++++++++++++--- src/r_things.h | 10 ++++++---- 5 files changed, 67 insertions(+), 7 deletions(-) diff --git a/src/dehacked.c b/src/dehacked.c index f34b3b58a..c038c2472 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -725,6 +725,7 @@ static void readfollower(MYFILE *f) followers[numfollowers].vertlag = 6; followers[numfollowers].bobspeed = TICRATE*2; followers[numfollowers].bobamp = 4; + followers[numfollowers].hitconfirmtime = TICRATE; do { @@ -835,6 +836,18 @@ static void readfollower(MYFILE *f) DEH_WriteUndoline(word, va("%d", followers[numfollowers].winstate), UNDO_NONE); followers[numfollowers].winstate = get_number(word2); } + else if (fastcmp(word, "HITSTATE") || (fastcmp(word, "HITCONFIRMSTATE"))) + { + if (word2) + strupr(word2); + DEH_WriteUndoline(word, va("%d", followers[numfollowers].hitconfirmstate), UNDO_NONE); + followers[numfollowers].hitconfirmstate = get_number(word2); + } + else if (fastcmp(word, "HITTIME") || (fastcmp(word, "HITCONFIRMTIME"))) + { + DEH_WriteUndoline(word, va("%d", followers[numfollowers].hitconfirmtime), UNDO_NONE); + followers[numfollowers].hitconfirmtime = (INT32)atoi(word2); + } else deh_warning("Follower %d: unknown word '%s'", numfollowers, word); } @@ -893,6 +906,10 @@ static void readfollower(MYFILE *f) if (followers[numfollowers].bobspeed < 0) followers[numfollowers].bobspeed = 1; + // hit confirm time must be > 0 + if (followers[numfollowers].hitconfirmtime < 1) + followers[numfollowers].hitconfirmtime = 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. @@ -909,6 +926,7 @@ if (!followers[numfollowers].field) \ NOSTATE(hurtstate, "hurtstate"); NOSTATE(losestate, "losestate"); NOSTATE(winstate, "winstate"); + NOSTATE(winstate, "hitconfirmstate"); #undef NOSTATE CONS_Printf("Added follower '%s'\n", dname); diff --git a/src/k_kart.c b/src/k_kart.c index 8e607191d..cb8b71a00 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -1642,6 +1642,13 @@ void K_PlayOvertakeSound(mobj_t *source) void K_PlayHitEmSound(mobj_t *source) { + + if (source->player->follower) + { + follower_t fl = followers[source->player->followerskin]; + source->player->follower->movecount = fl.hitconfirmtime; // movecount is used to play the hitconfirm animation for followers. + } + if (cv_kartvoices.value) S_StartSound(source, sfx_khitem); else diff --git a/src/lua_playerlib.c b/src/lua_playerlib.c index 1c37f4c45..61db7a2f7 100644 --- a/src/lua_playerlib.c +++ b/src/lua_playerlib.c @@ -319,6 +319,10 @@ static int player_get(lua_State *L) lua_pushinteger(L, plr->awayviewtics); else if (fastcmp(field,"awayviewaiming")) lua_pushangle(L, plr->awayviewaiming); + else if (fastcmp(field,"follower")) + LUA_PushUserdata(L, plr->follower, META_MOBJ); + else if (fastcmp(field,"followerskin")) + lua_pushinteger(L, plr->followerskin); else if (fastcmp(field,"spectator")) lua_pushboolean(L, plr->spectator); else if (fastcmp(field,"bot")) @@ -609,6 +613,10 @@ static int player_set(lua_State *L) } else if (fastcmp(field,"awayviewaiming")) plr->awayviewaiming = luaL_checkangle(L, 3); + else if (fastcmp(field,"follower")) // it's probably best we don't allow the follower mobj to change. + return NOSET; + else if (fastcmp(field,"followerskin")) + plr->followerskin = luaL_checkinteger(L, 3); else if (fastcmp(field,"spectator")) plr->spectator = lua_toboolean(L, 3); else if (fastcmp(field,"bot")) diff --git a/src/p_user.c b/src/p_user.c index 1b8748b2e..2f0998d7f 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -8929,6 +8929,15 @@ void P_DoTimeOver(player_t *player) */ static void P_SetFollowerState(mobj_t *f, INT32 state) { + // extravalue2 stores the last "first state" we used. + // because states default to idlestates, if we use an animation that uses an "ongoing" state line, don't reset it! + // this prevents it from looking very dumb + if (state == f->extravalue2) + return; + + // we will save the state into extravalue2. + f->extravalue2 = state; + P_SetMobjStateNF(f, state); if (f->state->tics > 0) f->tics++; @@ -9000,6 +9009,7 @@ static void P_HandleFollower(player_t *player) 2 = hurt 3 = win 4 = lose + 5 = hitconfirm (< this one uses ->movecount as timer to know when to end, and goes back to normal states afterwards, unless hurt) */ } else // follower exists, woo! @@ -9019,13 +9029,19 @@ static void P_HandleFollower(player_t *player) P_SetScale(player->follower, FixedMul(fl.scale, player->mo->scale)); K_MatchGenericExtraFlags(player->follower, player->mo); + // Make the follower invisible if we no contest'd rather than removing it. No one will notice the diff seriously. + + if (player->pflags & PF_TIMEOVER) // there is more to it than that to check for a full no contest but this isn't used for anything else. + player->follower->flags2 &= MF2_DONTDRAW; + + // handle follower animations. Yes, it looks like very bad kiddie script so what, do you have any better idea genius? Go get a life instead of criticizing my unpaid work!!!!!! - // hurt or dead - if (player->kartstuff[k_spinouttimer] || player->mo->health <= 0) + if (player->kartstuff[k_spinouttimer] || player->mo->state == &states[S_KART_SPIN] || player->mo->health <= 0) { - player->follower->angle = -leveltime*48*ANG1; // spin out + player->follower->movecount = 0; // cancel hit confirm. + player->follower->angle = player->frameangle; // spin out if (player->follower->extravalue1 != 2) { player->follower->extravalue1 = 2; @@ -9034,6 +9050,15 @@ static void P_HandleFollower(player_t *player) if (player->mo->health <= 0) // if dead, follow the player's z momentum exactly so they both look like they die at the same speed. player->follower->momz = player->mo->momz; } + else if (player->follower->movecount) + { + if (player->follower->extravalue1 != 5) + { + player->follower->extravalue1 = 5; + P_SetFollowerState(player->follower, fl.hitconfirmstate); + } + player->follower->movecount--; + } else if (player->speed > 10*player->mo->scale) // animation for moving fast enough { // if we're moving fast enough, let's make the angle the direction we're moving towards. This is to avoid drifting looking awkward. diff --git a/src/r_things.h b/src/r_things.h index ca9215f9a..155c7dbd2 100644 --- a/src/r_things.h +++ b/src/r_things.h @@ -124,16 +124,16 @@ 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. - + fixed_t scale; // Scale relative to the player's. - + // 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 dist; // distance relative to the player. (In a circle) INT32 zoffs; // Z offset relative to the player's height. Cannot be negative. - + // movement options - + INT32 horzlag; // Lag for X/Y displacement. Default is 2. Must be > 0 because we divide by this number. INT32 vertlag; // not Vert from Neptunia lagging, this is for Z displacement lag Default is 6. Must be > 0 because we divide by this number. INT32 bobamp; // Bob amplitude. Default is 4. @@ -147,6 +147,8 @@ typedef struct follower_s 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 + INT32 hitconfirmstate; // state for hit confirm + INT32 hitconfirmtime; // time to keep the above playing for } follower_t; // ----------- From 3d5f2e4e6717f26ec2ad8f64d055136526fc61dc Mon Sep 17 00:00:00 2001 From: Latapostrophe Date: Tue, 5 Mar 2019 12:23:23 +0100 Subject: [PATCH 14/44] I'm an idiot. Fix very dumb crash caused by lazy copypaste when trying to apply fallback state on followers --- src/dehacked.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dehacked.c b/src/dehacked.c index c038c2472..acf68a582 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -926,7 +926,7 @@ if (!followers[numfollowers].field) \ NOSTATE(hurtstate, "hurtstate"); NOSTATE(losestate, "losestate"); NOSTATE(winstate, "winstate"); - NOSTATE(winstate, "hitconfirmstate"); + NOSTATE(hitconfirmstate, "hitconfirmstate"); #undef NOSTATE CONS_Printf("Added follower '%s'\n", dname); From 5475eba690316a7f1f80adcf4c4188a6ec2a5687 Mon Sep 17 00:00:00 2001 From: Alam Arias Date: Wed, 6 Mar 2019 11:36:56 -0500 Subject: [PATCH 15/44] reorder file loads --- src/d_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/d_main.c b/src/d_main.c index 1105ba68a..e4c524079 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -931,10 +931,10 @@ static void IdentifyVersion(void) D_AddFile(va(pandf,srb2waddir,"textures.kart")); D_AddFile(va(pandf,srb2waddir,"chars.kart")); D_AddFile(va(pandf,srb2waddir,"maps.kart")); - D_AddFile(va(pandf,srb2waddir,"followers.kart")); // merge this in GFX later, this is mostly to avoid uploading a MASSIVE patch.kart /gfx.kart for testing. -Lat' #ifdef USE_PATCH_KART D_AddFile(va(pandf,srb2waddir,"patch.kart")); #endif + D_AddFile(va(pandf,srb2waddir,"followers.kart")); // merge this in GFX later, this is mostly to avoid uploading a MASSIVE patch.kart /gfx.kart for testing. -Lat' #if !defined (HAVE_SDL) || defined (HAVE_MIXER) #define MUSICTEST(str) \ From e2c72c08cd0d231178f36c55bc746bd6fba10964 Mon Sep 17 00:00:00 2001 From: Latapostrophe Date: Thu, 7 Mar 2019 23:10:07 +0100 Subject: [PATCH 16/44] Fix map load crash and other additions / clean ups --- src/dehacked.c | 66 +++++++++++++++++++++++--------------------------- src/p_mobj.c | 9 +++++++ src/p_setup.c | 3 +++ src/p_user.c | 12 +++++---- 4 files changed, 49 insertions(+), 41 deletions(-) diff --git a/src/dehacked.c b/src/dehacked.c index acf68a582..bbc78d153 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -877,38 +877,32 @@ static void readfollower(MYFILE *f) } strcpy(followers[numfollowers].skinname, testname); + strcpy(dname, followers[numfollowers].skinname); // display name, just used for printing succesful stuff or errors later down the line. - // get ready to print the name... - strcpy(dname, followers[numfollowers].skinname); + // now that the skin name is ready, post process the actual name to turn the underscores into spaces! + for (INT32 i = 0; followers[numfollowers].name[i]; i++) + { + if (followers[numfollowers].name[i] == '_') + followers[numfollowers].name[i] = ' '; + } - if (followers[numfollowers].dist < 0) - followers[numfollowers].dist = 0; + // fallbacks for variables + // Print a warning if the variable is on a weird value and set it back to the minimum available if that's the case. +#define FALLBACK(field, field2, threshold, set) \ +if (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, set, set); \ +} \ - if (followers[numfollowers].zoffs < 0) - followers[numfollowers].zoffs = 0; - - // HORZLAG and VERTLAG must ABSOLUTELY be higher than 0. If 0, the game crashes, if negative, weird shit happens! - if (followers[numfollowers].horzlag <= 0) - followers[numfollowers].horzlag = 1; - - if (followers[numfollowers].vertlag <= 0) - followers[numfollowers].vertlag = 1; - - // scale must be positive for obvious reasons, and so must both of the bob related variables - if (followers[numfollowers].scale <= 0) - followers[numfollowers].scale = 1; - - // Bob amplitude can totally be 0 - if (followers[numfollowers].bobamp < 0) - followers[numfollowers].bobamp = 1; - - // so can bob speed - if (followers[numfollowers].bobspeed < 0) - followers[numfollowers].bobspeed = 1; - - // hit confirm time must be > 0 - if (followers[numfollowers].hitconfirmtime < 1) - followers[numfollowers].hitconfirmtime = 1; + FALLBACK(dist, "DIST", 0, 0); + FALLBACK(zoffs, "ZOFFS", 0, 0); + FALLBACK(horzlag, "HORZLAG", 1, 1); + FALLBACK(vertlag, "VERTLAG", 1, 1); + FALLBACK(bobamp, "BOBAMP", 0, 0); + FALLBACK(bobspeed, "BOBSPEED", 0, 0); + FALLBACK(hitconfirmtime, "HITCONFIRMTIME", 1, 1); +#undef FALLBACK // 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. @@ -918,15 +912,15 @@ 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", dname, field2); \ } \ - NOSTATE(idlestate, "idlestate"); - NOSTATE(followstate, "followstate"); - NOSTATE(hurtstate, "hurtstate"); - NOSTATE(losestate, "losestate"); - NOSTATE(winstate, "winstate"); - NOSTATE(hitconfirmstate, "hitconfirmstate"); + NOSTATE(idlestate, "IDLESTATE"); + NOSTATE(followstate, "FOLLOWSTATE"); + NOSTATE(hurtstate, "HURTSTATE"); + NOSTATE(losestate, "LOSESTATE"); + NOSTATE(winstate, "WINSTATE"); + NOSTATE(hitconfirmstate, "HITCONFIRMSTATE"); #undef NOSTATE CONS_Printf("Added follower '%s'\n", dname); diff --git a/src/p_mobj.c b/src/p_mobj.c index fd42a1d71..461f50aaa 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -6556,6 +6556,13 @@ void P_MobjThinker(mobj_t *mobj) #endif switch (mobj->type) { + case MT_FOLLOWER: + // small thinker for follower: + // We cleanse ourselves from existence if our target player doesn't exist for whatever reason. (generally players leaving) + if (!mobj->target || P_MobjWasRemoved(mobj->target) || !mobj->target->player || mobj->target->player->spectator || mobj->target->player->followerskin < 0) + P_RemoveMobj(mobj); + + return; case MT_HOOP: if (mobj->fuse > 1) P_MoveHoop(mobj); @@ -10962,6 +10969,8 @@ void P_SpawnPlayer(INT32 playernum) //awayview stuff p->awayviewmobj = NULL; p->awayviewtics = 0; + + p->follower = NULL; // cleanse follower from existence // set the scale to the mobj's destscale so settings get correctly set. if we don't, they sometimes don't. if (cv_kartdebugshrink.value && !modeattacking && !p->bot) diff --git a/src/p_setup.c b/src/p_setup.c index 1acfb7a32..7334ba78b 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -2356,6 +2356,9 @@ static void P_LevelInitStuff(void) // and this stupid flag as a result players[i].pflags &= ~PF_TRANSFERTOCLOSEST; + + // Wipe follower from existence to avoid crashes + players[i].follower = NULL; } // SRB2Kart: map load variables diff --git a/src/p_user.c b/src/p_user.c index f062a4618..5b187d710 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -8960,7 +8960,7 @@ static void P_HandleFollower(player_t *player) // How about making sure our follower exists and is added before trying to spawn it n' all? if (player->followerskin > numfollowers-1 || player->followerskin < -1) { - CONS_Printf("Follower skin invlaid. Setting to -1.\n"); + //CONS_Printf("Follower skin invlaid. Setting to -1.\n"); player->followerskin = -1; return; } @@ -8970,7 +8970,6 @@ static void P_HandleFollower(player_t *player) return; if (player->followerskin < 0) return; - // Before we do anything, let's be sure of where we're supposed to be fl = followers[player->followerskin]; @@ -9029,12 +9028,17 @@ static void P_HandleFollower(player_t *player) P_SetScale(player->follower, FixedMul(fl.scale, player->mo->scale)); K_MatchGenericExtraFlags(player->follower, player->mo); + // For comeback in battle. + player->follower->flags2 = (player->follower->flags2 & ~MF2_SHADOW)|(player->mo->flags2 & MF2_SHADOW); + // Make the follower invisible if we no contest'd rather than removing it. No one will notice the diff seriously. if (player->pflags & PF_TIMEOVER) // there is more to it than that to check for a full no contest but this isn't used for anything else. player->follower->flags2 &= MF2_DONTDRAW; - + if (player->speed) + player->follower->angle = R_PointToAngle2(0, 0, player->follower->momx, player->follower->momy); + // if we're moving let's make the angle the direction we're moving towards. This is to avoid drifting / reverse looking awkward. // handle follower animations. Yes, it looks like very bad kiddie script so what, do you have any better idea genius? Go get a life instead of criticizing my unpaid work!!!!!! // hurt or dead @@ -9061,8 +9065,6 @@ static void P_HandleFollower(player_t *player) } else if (player->speed > 10*player->mo->scale) // animation for moving fast enough { - // if we're moving fast enough, let's make the angle the direction we're moving towards. This is to avoid drifting looking awkward. - player->follower->angle = R_PointToAngle2(0, 0, player->follower->momx, player->follower->momy); if (player->follower->extravalue1 != 1) { From 5418bdef01bb282dd30d767f1e2a4edf0d2fed18 Mon Sep 17 00:00:00 2001 From: Alam Arias Date: Thu, 7 Mar 2019 17:30:28 -0500 Subject: [PATCH 17/44] Always compile in GNU C89 mode --- src/Makefile.cfg | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Makefile.cfg b/src/Makefile.cfg index 236d7ae2f..78f52a673 100644 --- a/src/Makefile.cfg +++ b/src/Makefile.cfg @@ -122,6 +122,9 @@ endif ifndef GCC295 WFLAGS+=-Wendif-labels endif +ifdef GCC40 + WFLAGS+=-std=gnu89 +endif ifdef GCC41 WFLAGS+=-Wshadow endif From d464ab5ac560da83b9c8c2d7c99bad73de83de84 Mon Sep 17 00:00:00 2001 From: Alam Arias Date: Thu, 7 Mar 2019 17:34:46 -0500 Subject: [PATCH 18/44] fixup SOC changes for C 89 mode --- src/dehacked.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/dehacked.c b/src/dehacked.c index bbc78d153..870f024f9 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -707,6 +707,7 @@ static void readfollower(MYFILE *f) boolean nameset; INT32 fallbackstate = 0; INT32 res; + INT32 i; if (numfollowers > MAXSKINS) { @@ -880,7 +881,7 @@ static void readfollower(MYFILE *f) strcpy(dname, followers[numfollowers].skinname); // display name, just used for printing succesful stuff or errors later down the line. // now that the skin name is ready, post process the actual name to turn the underscores into spaces! - for (INT32 i = 0; followers[numfollowers].name[i]; i++) + for (i = 0; followers[numfollowers].name[i]; i++) { if (followers[numfollowers].name[i] == '_') followers[numfollowers].name[i] = ' '; From a8428f0ba09841d6d1fbe4765e5649388cf5b029 Mon Sep 17 00:00:00 2001 From: Wolfy Date: Sat, 6 Apr 2019 13:52:34 -0500 Subject: [PATCH 19/44] Make this compile again --- src/d_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/d_main.c b/src/d_main.c index da17807fc..3cc7758f5 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -940,7 +940,7 @@ static void IdentifyVersion(void) #ifdef USE_PATCH_KART D_AddFile(va(pandf,srb2waddir,"patch.kart"), startupwadfiles); #endif - D_AddFile(va(pandf,srb2waddir,"followers.kart")); // merge this in GFX later, this is mostly to avoid uploading a MASSIVE patch.kart /gfx.kart for testing. -Lat' + D_AddFile(va(pandf,srb2waddir,"followers.kart"), startupwadfiles); // merge this in GFX later, this is mostly to avoid uploading a MASSIVE patch.kart /gfx.kart for testing. -Lat' #if !defined (HAVE_SDL) || defined (HAVE_MIXER) #define MUSICTEST(str) \ From 7dff052a64c74b17cfb3b96ab3718d5cd0abc156 Mon Sep 17 00:00:00 2001 From: wolfy852 Date: Sat, 11 May 2019 02:13:31 -0500 Subject: [PATCH 20/44] Expose followerready, match d_player.h organization --- src/lua_playerlib.c | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/src/lua_playerlib.c b/src/lua_playerlib.c index 795dab1a2..02d81f0ca 100644 --- a/src/lua_playerlib.c +++ b/src/lua_playerlib.c @@ -154,6 +154,12 @@ static int player_get(lua_State *L) lua_pushinteger(L, plr->kartspeed); else if (fastcmp(field,"kartweight")) lua_pushinteger(L, plr->kartweight); + else if (fastcmp(field,"followerskin")) + lua_pushinteger(L, plr->followerskin); + else if (fastcmp(field,"followerready")) + lua_pushboolean(L, plr->followerready); + else if (fastcmp(field,"follower")) + LUA_PushUserdata(L, plr->follower, META_MOBJ); // else if (fastcmp(field,"normalspeed")) lua_pushfixed(L, plr->normalspeed); @@ -319,10 +325,7 @@ static int player_get(lua_State *L) lua_pushinteger(L, plr->awayviewtics); else if (fastcmp(field,"awayviewaiming")) lua_pushangle(L, plr->awayviewaiming); - else if (fastcmp(field,"follower")) - LUA_PushUserdata(L, plr->follower, META_MOBJ); - else if (fastcmp(field,"followerskin")) - lua_pushinteger(L, plr->followerskin); + else if (fastcmp(field,"spectator")) lua_pushboolean(L, plr->spectator); else if (fastcmp(field,"bot")) @@ -434,6 +437,12 @@ static int player_set(lua_State *L) plr->kartspeed = (UINT8)luaL_checkinteger(L, 3); else if (fastcmp(field,"kartweight")) plr->kartweight = (UINT8)luaL_checkinteger(L, 3); + else if (fastcmp(field,"followerskin")) + plr->followerskin = luaL_checkinteger(L, 3); + else if (fastcmp(field,"followerready")) + plr->followerready = luaL_checkboolean(L, 3); + else if (fastcmp(field,"follower")) // it's probably best we don't allow the follower mobj to change. + return NOSET; // else if (fastcmp(field,"normalspeed")) plr->normalspeed = luaL_checkfixed(L, 3); @@ -613,10 +622,6 @@ static int player_set(lua_State *L) } else if (fastcmp(field,"awayviewaiming")) plr->awayviewaiming = luaL_checkangle(L, 3); - else if (fastcmp(field,"follower")) // it's probably best we don't allow the follower mobj to change. - return NOSET; - else if (fastcmp(field,"followerskin")) - plr->followerskin = luaL_checkinteger(L, 3); else if (fastcmp(field,"spectator")) plr->spectator = lua_toboolean(L, 3); else if (fastcmp(field,"bot")) From 54d61b188c004e5ea100e365b515d9a2404ed1fe Mon Sep 17 00:00:00 2001 From: wolfy852 Date: Sun, 12 May 2019 03:07:17 -0500 Subject: [PATCH 21/44] Have "None" work as an alias for -1 --- src/d_netcmd.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 4ec54d286..56e4684f5 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -5335,6 +5335,13 @@ static void Follower_OnChange(void) 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"); + SendNameAndColor(); + return; + } + INT32 num = R_FollowerAvailable(str); char set[10]; From 1a7af92d9fe911b24638bc45e782e7952df5068a Mon Sep 17 00:00:00 2001 From: Latapostrophe Date: Sun, 12 May 2019 10:34:10 +0200 Subject: [PATCH 22/44] fix being unable to remove follower outside of netgames --- 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 56e4684f5..8ea9a5028 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -1346,7 +1346,7 @@ static void SendNameAndColor(void) players[consoleplayer].mo->color = players[consoleplayer].skincolor; // Update follower for local games: - if (cv_follower.value >= 0 && cv_follower.value != players[consoleplayer].followerskin) + if (cv_follower.value >= -1 && cv_follower.value != players[consoleplayer].followerskin) SetFollower(consoleplayer, cv_follower.value); if (metalrecording) @@ -1486,7 +1486,7 @@ static void SendNameAndColor2(void) players[secondplaya].mo->color = players[secondplaya].skincolor; // Update follower for local games: - if (cv_follower2.value >= 0 && cv_follower2.value != players[secondplaya].followerskin) + if (cv_follower2.value >= -1 && cv_follower2.value != players[secondplaya].followerskin) SetFollower(secondplaya, cv_follower2.value); if ((foundskin = R_SkinAvailable(cv_skin2.string)) != -1) @@ -1610,7 +1610,7 @@ static void SendNameAndColor3(void) players[thirdplaya].mo->color = players[thirdplaya].skincolor; // Update follower for local games: - if (cv_follower3.value >= 0 && cv_follower3.value != players[thirdplaya].followerskin) + if (cv_follower3.value >= -1 && cv_follower3.value != players[thirdplaya].followerskin) SetFollower(thirdplaya, cv_follower3.value); if ((foundskin = R_SkinAvailable(cv_skin3.string)) != -1) @@ -1742,7 +1742,7 @@ static void SendNameAndColor4(void) players[fourthplaya].mo->color = players[fourthplaya].skincolor; // Update follower for local games: - if (cv_follower4.value >= 0 && cv_follower4.value != players[fourthplaya].followerskin) + if (cv_follower4.value >= -1 && cv_follower4.value != players[fourthplaya].followerskin) SetFollower(fourthplaya, cv_follower4.value); if ((foundskin = R_SkinAvailable(cv_skin4.string)) != -1) From a0e27c22e7048b119c810f73e32622ea8cc4ff3e Mon Sep 17 00:00:00 2001 From: Latapostrophe Date: Sun, 12 May 2019 13:41:46 +0200 Subject: [PATCH 23/44] make followers work with replays --- src/d_netcmd.c | 25 +++++++++++++++++++ src/g_game.c | 66 +++++++++++++++++++++++++++++++++++++++++++++++--- src/g_game.h | 1 + src/m_menu.c | 4 +-- src/p_user.c | 2 +- src/r_things.c | 4 +++ 6 files changed, 96 insertions(+), 6 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index a7ae75db4..a4d384d77 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -5577,6 +5577,15 @@ static void Follower2_OnChange(void) strlwr(str); if (stricmp(cpy,"0") !=0 && !atoi(cpy)) // yep, that's a string alright... { + + if (stricmp(cpy, "None") == 0) + { + CV_StealthSet(&cv_follower2, "-1"); + SendNameAndColor2(); + return; + } + + INT32 num = R_FollowerAvailable(str); char set[10]; if (num == -1) // that's an error. @@ -5599,6 +5608,14 @@ static void Follower3_OnChange(void) strlwr(str); if (stricmp(cpy,"0") !=0 && !atoi(cpy)) // yep, that's a string alright... { + + if (stricmp(cpy, "None") == 0) + { + CV_StealthSet(&cv_follower3, "-1"); + SendNameAndColor3(); + return; + } + INT32 num = R_FollowerAvailable(str); char set[10]; if (num == -1) // that's an error. @@ -5621,6 +5638,14 @@ static void Follower4_OnChange(void) strlwr(str); if (stricmp(cpy,"0") !=0 && !atoi(cpy)) // yep, that's a string alright... { + + if (stricmp(cpy, "None") == 0) + { + CV_StealthSet(&cv_follower4, "-1"); + SendNameAndColor4(); + return; + } + INT32 num = R_FollowerAvailable(str); char set[10]; if (num == -1) // that's an error. diff --git a/src/g_game.c b/src/g_game.c index 0831f7855..74e4b1906 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -3251,7 +3251,7 @@ void G_AddPlayer(INT32 playernum) p->jointime = 0; p->playerstate = PST_REBORN; - demo_extradata[playernum] |= DXD_PLAYSTATE|DXD_COLOR|DXD_NAME|DXD_SKIN; // Set everything + demo_extradata[playernum] |= DXD_PLAYSTATE|DXD_COLOR|DXD_NAME|DXD_SKIN|DXD_FOLLOWER; // Set everything } void G_ExitLevel(void) @@ -3284,7 +3284,7 @@ const char *Gametype_Names[NUMGAMETYPES] = { "Race", // GT_RACE "Battle" // GT_MATCH - + /*"Co-op", // GT_COOP "Competition", // GT_COMPETITION "Team Match", // GT_TEAMMATCH @@ -4926,6 +4926,13 @@ void G_ReadDemoExtraData(void) M_Memcpy(player_names[p],demo_p,16); demo_p += 16; } + if (extradata & DXD_FOLLOWER) + { + // Set our follower + M_Memcpy(name, demo_p, 16); + demo_p += 16; + SetPlayerFollower(p, name); + } if (extradata & DXD_PLAYSTATE) { extradata = READUINT8(demo_p); @@ -5028,6 +5035,7 @@ void G_WriteDemoExtraData(void) WRITEUINT8(demo_p, skins[players[i].skin].kartspeed); WRITEUINT8(demo_p, skins[players[i].skin].kartweight); + } if (demo_extradata[i] & DXD_COLOR) { @@ -5045,6 +5053,15 @@ void G_WriteDemoExtraData(void) M_Memcpy(demo_p,name,16); demo_p += 16; } + if (demo_extradata[i] & DXD_FOLLOWER) + { + // write follower + memset(name, 0, 16); + strncpy(name, followers[players[i].followerskin].skinname, 16); + CONS_Printf("%s\n", name); + M_Memcpy(demo_p, name, 16); + demo_p += 16; + } if (demo_extradata[i] & DXD_PLAYSTATE) { demo_writerng = 1; @@ -5668,6 +5685,8 @@ void G_GhostTicker(void) g->p += 16; // Same tbh if (ziptic & DXD_NAME) g->p += 16; // yea + if (ziptic & DXD_FOLLOWER) + g->p += 16; // ok if (ziptic & DXD_PLAYSTATE && READUINT8(g->p) != DXD_PST_PLAYING) I_Error("Ghost is not a record attack ghost"); //@TODO lmao don't blow up like this } @@ -6396,6 +6415,12 @@ void G_BeginRecording(void) CV_SaveNetVars(&demo_p, true); // Now store some info for each in-game player + + // Lat' 12/05/19: Do note that for the first game you load, everything that gets saved here is total garbage; + // The name will always be Player , the skin sonic, the color None and the follower 0. This is only correct on subsequent games. + // In the case of said first game, the skin and the likes are updated with Got_NameAndColor, which are then saved in extradata for the demo with DXD_SKIN in r_things.c for instance. + + for (p = 0; p < MAXPLAYERS; p++) { if (playeringame[p]) { player = &players[p]; @@ -6420,6 +6445,12 @@ void G_BeginRecording(void) M_Memcpy(demo_p,name,16); demo_p += 16; + // Save follower's skin name + memset(name, 0, 16); + strncpy(name, followers[player->followerskin].skinname, 16); + M_Memcpy(demo_p,name,16); + demo_p += 16; + // Score, since Kart uses this to determine where you start on the map WRITEUINT32(demo_p, player->score); @@ -7031,7 +7062,7 @@ void G_DoPlayDemo(char *defdemoname) { UINT8 i, p; lumpnum_t l; - char skin[17],color[17],*n,*pdemoname; + char skin[17],color[17],follower[17],*n,*pdemoname; UINT8 version,subversion; UINT32 randseed; char msg[1024]; @@ -7045,6 +7076,7 @@ void G_DoPlayDemo(char *defdemoname) skin[16] = '\0'; color[16] = '\0'; + follower[16] = '\0'; // No demo name means we're restarting the current demo if (defdemoname == NULL) @@ -7280,6 +7312,10 @@ void G_DoPlayDemo(char *defdemoname) M_Memcpy(color,demo_p,16); demo_p += 16; + // Follower + M_Memcpy(follower,demo_p,16); + demo_p += 16; + demo_p += 5; // Backwards compat - some stats // SRB2kart kartspeed[0] = READUINT8(demo_p); @@ -7321,6 +7357,19 @@ void G_DoPlayDemo(char *defdemoname) break; } + // Follower + if (!SetPlayerFollower(0, follower)) + { + snprintf(msg, 1024, M_GetText("%s features a follower that is not currently loaded.\n"), pdemoname); + CONS_Alert(CONS_ERROR, "%s", msg); + M_StartMessage(msg, NULL, MM_NOTHING); + Z_Free(pdemoname); + Z_Free(demobuffer); + demo.playback = false; + demo.title = false; + return; + } + // net var data CV_LoadNetVars(&demo_p); @@ -7474,6 +7523,11 @@ void G_DoPlayDemo(char *defdemoname) break; } + // Follower + M_Memcpy(follower, demo_p, 16); + demo_p += 16; + SetPlayerFollower(p, follower); + // Score, since Kart uses this to determine where you start on the map players[p].score = READUINT32(demo_p); @@ -7679,6 +7733,9 @@ void G_AddGhost(char *defdemoname) M_Memcpy(color, p,16); p += 16; + // Follower data was here, doesn't matter for ghosts + p += 16; + // Ghosts do not have a player structure to put this in. p++; // charability p++; // charability2 @@ -7741,6 +7798,9 @@ void G_AddGhost(char *defdemoname) M_Memcpy(color, p, 16); p += 16; + // Follower data was here, skip it, we don't care about it for ghosts. + p += 16; + p += 4; // score kartspeed = READUINT8(p); diff --git a/src/g_game.h b/src/g_game.h index a69f91421..06b9ef829 100644 --- a/src/g_game.h +++ b/src/g_game.h @@ -214,6 +214,7 @@ extern UINT8 demo_writerng; #define DXD_NAME 0x04 // name changed #define DXD_COLOR 0x08 // color changed #define DXD_PLAYSTATE 0x10 // state changed between playing, spectating, or not in-game +#define DXD_FOLLOWER 0x20 // follower was changed #define DXD_PST_PLAYING 0x01 #define DXD_PST_SPECTATING 0x02 diff --git a/src/m_menu.c b/src/m_menu.c index 0bedf62c6..e07abff9d 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -9428,8 +9428,8 @@ static void M_HandleSetupMultiPlayer(INT32 choice) size_t l; boolean exitmenu = false; // exit to previous menu and send name change - if ((choice == gamecontrol[gc_lookback][0] || choice == gamecontrol[gc_lookback][1]) && itemOn == 2) - choice == KEY_BACKSPACE // Hack to allow resetting prefcolor on controllers + if ((choice == gamecontrol[gc_fire][0] || choice == gamecontrol[gc_fire][1]) && itemOn == 2) + choice = KEY_BACKSPACE; // Hack to allow resetting prefcolor on controllers switch (choice) { diff --git a/src/p_user.c b/src/p_user.c index ea99cdcf9..8b59fd22c 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -7377,7 +7377,7 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall if (P_CameraThinker(player, thiscam, resetcalled)) return true; - + if (thiscam == &camera[1]) // Camera 2 { num = 1; diff --git a/src/r_things.c b/src/r_things.c index 75d2d4349..d0b34eddd 100644 --- a/src/r_things.c +++ b/src/r_things.c @@ -2719,6 +2719,7 @@ void SetPlayerSkinByNum(INT32 playernum, INT32 skinnum) if (player->mo) P_SetScale(player->mo, player->mo->scale); + // for replays: We have changed our skin mid-game; let the game know so it can do the same in the replay! demo_extradata[playernum] |= DXD_SKIN; return; @@ -2751,6 +2752,9 @@ void SetFollower(INT32 playernum, INT32 skinnum) player->follower = NULL; } + // for replays: We have changed our follower mid-game; let the game know so it can do the same in the replay! + demo_extradata[playernum] |= DXD_FOLLOWER; + return; } From c54cc42e74c33678bb5a2eb5c5e25803455bdc8f Mon Sep 17 00:00:00 2001 From: Latapostrophe Date: Mon, 13 May 2019 19:53:17 +0200 Subject: [PATCH 24/44] Add command to disable followers clientside --- src/d_netcmd.c | 5 +++++ src/d_netcmd.h | 1 + src/g_game.c | 1 - src/k_kart.c | 4 ++-- src/m_menu.c | 27 +++++++++++++++------------ src/p_user.c | 6 +++--- 6 files changed, 26 insertions(+), 18 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index a4d384d77..0b27dab98 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -282,6 +282,10 @@ consvar_t cv_follower2 = {"follower2", "-1", CV_SAVE|CV_CALL|CV_NOINIT, NULL, Fo 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}; +// Follower toggle +static CV_PossibleValue_t followers_cons_t[] = {{0, "Yours only"}, {1, "Everyone's"}, {0, NULL}}; +consvar_t cv_showfollowers = {"showfollowers", "Everyone's", CV_SAVE, followers_cons_t, 0, 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; @@ -804,6 +808,7 @@ void D_RegisterClientCommands(void) CV_RegisterVar(&cv_playercolor); CV_RegisterVar(&cv_skin); // r_things.c (skin NAME) CV_RegisterVar(&cv_follower); + CV_RegisterVar(&cv_showfollowers); // secondary player (splitscreen) CV_RegisterVar(&cv_playername2); CV_RegisterVar(&cv_playercolor2); diff --git a/src/d_netcmd.h b/src/d_netcmd.h index 9d2229c4d..966351181 100644 --- a/src/d_netcmd.h +++ b/src/d_netcmd.h @@ -22,6 +22,7 @@ extern consvar_t cv_playername; extern consvar_t cv_playercolor; extern consvar_t cv_skin; extern consvar_t cv_follower; +extern consvar_t cv_showfollowers; // secondary splitscreen player extern consvar_t cv_playername2; extern consvar_t cv_playercolor2; diff --git a/src/g_game.c b/src/g_game.c index 74e4b1906..dea7d6aa7 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -5058,7 +5058,6 @@ void G_WriteDemoExtraData(void) // write follower memset(name, 0, 16); strncpy(name, followers[players[i].followerskin].skinname, 16); - CONS_Printf("%s\n", name); M_Memcpy(demo_p, name, 16); demo_p += 16; } diff --git a/src/k_kart.c b/src/k_kart.c index bb2cbc701..1ac9fa6d7 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -1739,13 +1739,13 @@ void K_PlayOvertakeSound(mobj_t *source) void K_PlayHitEmSound(mobj_t *source) { - + if (source->player->follower) { follower_t fl = followers[source->player->followerskin]; source->player->follower->movecount = fl.hitconfirmtime; // movecount is used to play the hitconfirm animation for followers. } - + if (cv_kartvoices.value) S_StartSound(source, sfx_khitem); else diff --git a/src/m_menu.c b/src/m_menu.c index e07abff9d..5437562d0 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -1429,24 +1429,27 @@ enum static menuitem_t OP_HUDOptionsMenu[] = { - {IT_STRING | IT_CVAR, NULL, "Show HUD (F3)", &cv_showhud, 10}, - {IT_STRING | IT_CVAR | IT_CV_SLIDER, - NULL, "HUD Visibility", &cv_translucenthud, 20}, - {IT_STRING | IT_SUBMENU, NULL, "Online HUD options...",&OP_ChatOptionsDef, 35}, - {IT_STRING | IT_CVAR, NULL, "Background Glass", &cons_backcolor, 45}, + {IT_STRING | IT_CVAR, NULL, "Show Followers", &cv_showfollowers, 10}, + + {IT_STRING | IT_CVAR, NULL, "Show HUD (F3)", &cv_showhud, 20}, + {IT_STRING | IT_CVAR | IT_CV_SLIDER, + NULL, "HUD Visibility", &cv_translucenthud, 30}, + + {IT_STRING | IT_SUBMENU, NULL, "Online HUD options...",&OP_ChatOptionsDef, 45}, + {IT_STRING | IT_CVAR, NULL, "Background Glass", &cons_backcolor, 55}, {IT_STRING | IT_CVAR | IT_CV_SLIDER, - NULL, "Minimap Visibility", &cv_kartminimap, 60}, - {IT_STRING | IT_CVAR, NULL, "Speedometer Display", &cv_kartspeedometer, 70}, - {IT_STRING | IT_CVAR, NULL, "Show \"CHECK\"", &cv_kartcheck, 80}, + NULL, "Minimap Visibility", &cv_kartminimap, 70}, + {IT_STRING | IT_CVAR, NULL, "Speedometer Display", &cv_kartspeedometer, 80}, + {IT_STRING | IT_CVAR, NULL, "Show \"CHECK\"", &cv_kartcheck, 90}, - {IT_STRING | IT_CVAR, NULL, "Menu Highlights", &cons_menuhighlight, 95}, + {IT_STRING | IT_CVAR, NULL, "Menu Highlights", &cons_menuhighlight, 105}, // highlight info - (GOOD HIGHLIGHT, WARNING HIGHLIGHT) - 105 (see M_DrawHUDOptions) - {IT_STRING | IT_CVAR, NULL, "Console Text Size", &cv_constextsize, 120}, + {IT_STRING | IT_CVAR, NULL, "Console Text Size", &cv_constextsize, 130}, - {IT_STRING | IT_CVAR, NULL, "Show \"FOCUS LOST\"", &cv_showfocuslost, 135}, + {IT_STRING | IT_CVAR, NULL, "Show \"FOCUS LOST\"", &cv_showfocuslost, 145}, }; // Ok it's still called chatoptions but we'll put ping display in here to be clean @@ -10621,7 +10624,7 @@ static void M_DrawHUDOptions(void) const char *str1 = " Warning highlight"; const char *str2 = ","; const char *str3 = "Good highlight"; - INT32 x = BASEVIDWIDTH - currentMenu->x + 2, y = currentMenu->y + 105; + INT32 x = BASEVIDWIDTH - currentMenu->x + 2, y = currentMenu->y + 115; INT32 w0 = V_StringWidth(str0, 0), w1 = V_StringWidth(str1, 0), w2 = V_StringWidth(str2, 0), w3 = V_StringWidth(str3, 0); M_DrawGenericMenu(); diff --git a/src/p_user.c b/src/p_user.c index 8b59fd22c..756d94f95 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -8171,9 +8171,9 @@ static void P_HandleFollower(player_t *player) player->follower->flags2 = (player->follower->flags2 & ~MF2_SHADOW)|(player->mo->flags2 & MF2_SHADOW); // Make the follower invisible if we no contest'd rather than removing it. No one will notice the diff seriously. - - if (player->pflags & PF_TIMEOVER) // there is more to it than that to check for a full no contest but this isn't used for anything else. - player->follower->flags2 &= MF2_DONTDRAW; + // Also make the follower invisible if we choose not to have it displayed because it isn't ours. (also quick hacky check for f12) + if (player->pflags & PF_TIMEOVER || (!cv_showfollowers.value && (!P_IsDisplayPlayer(player) || displayplayers[0] != consoleplayer) )) + player->follower->flags2 |= MF2_DONTDRAW; if (player->speed) player->follower->angle = R_PointToAngle2(0, 0, player->follower->momx, player->follower->momy); From 4c8d3739624ad5a5226fb69fa69e6a08b4a6b370 Mon Sep 17 00:00:00 2001 From: Latapostrophe Date: Sun, 29 Mar 2020 16:40:21 +0200 Subject: [PATCH 25/44] Fix followers in anti gravity --- src/dehacked.c | 7 +++++++ src/k_kart.c | 15 +++++++++++++++ src/k_kart.h | 1 + src/p_user.c | 19 +++++++++---------- src/r_things.h | 1 + 5 files changed, 33 insertions(+), 10 deletions(-) diff --git a/src/dehacked.c b/src/dehacked.c index 3f760da15..e8a33b0bd 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -721,6 +721,7 @@ static void readfollower(MYFILE *f) followers[numfollowers].scale = FRACUNIT; followers[numfollowers].atangle = 230; followers[numfollowers].dist = 16; + followers[numfollowers].height = 16; followers[numfollowers].zoffs = 32; followers[numfollowers].horzlag = 2; followers[numfollowers].vertlag = 6; @@ -801,6 +802,11 @@ static void readfollower(MYFILE *f) DEH_WriteUndoline(word, va("%d", followers[numfollowers].dist), UNDO_NONE); followers[numfollowers].dist = (INT32)atoi(word2); } + else if (fastcmp(word, "HEIGHT")) + { + DEH_WriteUndoline(word, va("%d", followers[numfollowers].height), UNDO_NONE); + followers[numfollowers].height = (INT32)atoi(word2); + } else if (fastcmp(word, "IDLESTATE")) { if (word2) @@ -897,6 +903,7 @@ if (followers[numfollowers].field < threshold) \ } \ FALLBACK(dist, "DIST", 0, 0); + FALLBACK(height, "HEIGHT", 1, 1); FALLBACK(zoffs, "ZOFFS", 0, 0); FALLBACK(horzlag, "HORZLAG", 1, 1); FALLBACK(vertlag, "VERTLAG", 1, 1); diff --git a/src/k_kart.c b/src/k_kart.c index 7784f6a46..6b679cb2b 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -1930,6 +1930,21 @@ void K_MatchGenericExtraFlags(mobj_t *mo, mobj_t *master) mo->eflags = (mo->eflags & ~MFE_DRAWONLYFORP4)|(master->eflags & MFE_DRAWONLYFORP4); } +// same as above, but does not adjust Z height when flipping +void K_GenericExtraFlagsNoZAdjust(mobj_t *mo, mobj_t *master) +{ + // flipping + mo->eflags = (mo->eflags & ~MFE_VERTICALFLIP)|(master->eflags & MFE_VERTICALFLIP); + mo->flags2 = (mo->flags2 & ~MF2_OBJECTFLIP)|(master->flags2 & MF2_OBJECTFLIP); + + // visibility (usually for hyudoro) + mo->flags2 = (mo->flags2 & ~MF2_DONTDRAW)|(master->flags2 & MF2_DONTDRAW); + mo->eflags = (mo->eflags & ~MFE_DRAWONLYFORP1)|(master->eflags & MFE_DRAWONLYFORP1); + mo->eflags = (mo->eflags & ~MFE_DRAWONLYFORP2)|(master->eflags & MFE_DRAWONLYFORP2); + mo->eflags = (mo->eflags & ~MFE_DRAWONLYFORP3)|(master->eflags & MFE_DRAWONLYFORP3); + mo->eflags = (mo->eflags & ~MFE_DRAWONLYFORP4)|(master->eflags & MFE_DRAWONLYFORP4); +} + static void K_SpawnDashDustRelease(player_t *player) { fixed_t newx; diff --git a/src/k_kart.h b/src/k_kart.h index 273d8bcb1..331f53bcd 100644 --- a/src/k_kart.h +++ b/src/k_kart.h @@ -31,6 +31,7 @@ void K_KartBouncing(mobj_t *mobj1, mobj_t *mobj2, boolean bounce, boolean solid) void K_KartPainEnergyFling(player_t *player); void K_FlipFromObject(mobj_t *mo, mobj_t *master); void K_MatchGenericExtraFlags(mobj_t *mo, mobj_t *master); +void K_GenericExtraFlagsNoZAdjust(mobj_t *mo, mobj_t *master); void K_DoIngameRespawn(player_t *player); void K_RespawnChecker(player_t *player); void K_KartMoveAnimation(player_t *player); diff --git a/src/p_user.c b/src/p_user.c index 54b6a5393..d7b8a1951 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -8072,17 +8072,15 @@ static void P_HandleFollower(player_t *player) sy = player->mo->y + FixedMul((player->mo->scale*fl.dist), FINESINE((an)>>ANGLETOFINESHIFT)); // for the z coordinate, don't be a doof like Steel and forget that MFE_VERTICALFLIP exists :P - sz = player->mo->z + FixedMul(player->mo->scale, zoffs); - /*if (player->mo->eflags & MFE_VERTICALFLIP) // it's safe to assume that VERTICALFLIP accounts for MF2_OBJECTFLIP too - sz -= (player->mo->height + FixedMul(player->mo->scale, zoffs*2));*/ - // ^ handled by K_matchgenericextraflags oops - + sz = player->mo->z + FixedMul(player->mo->scale, zoffs)*P_MobjFlip(player->mo); + if (player->mo->eflags & MFE_VERTICALFLIP) + sz += fl.height*player->mo->scale; // finally, add a cool floating effect to the z height. // not stolen from k_kart I swear!! const fixed_t pi = (22<>ANGLETOFINESHIFT) & FINEMASK); - sz += sine; + sz += FixedMul(player->mo->scale, sine)*P_MobjFlip(player->mo); if (!player->follower) // follower doesn't exist / isn't valid { @@ -8111,13 +8109,13 @@ static void P_HandleFollower(player_t *player) // move the follower next to us (yes, this is really basic maths but it looks pretty damn clean in practice)! player->follower->momx = (sx - player->follower->x)/fl.horzlag; player->follower->momy = (sy - player->follower->y)/fl.horzlag; - player->follower->momz = (sz - player->follower->z)/fl.vertlag; // make z momentum a bit floatier, it'll look cute I promise! + player->follower->momz = (sz - player->follower->z)/fl.vertlag; player->follower->angle = player->mo->angle; player->follower->color = player->mo->color; player->follower->colorized = player->mo->colorized; P_SetScale(player->follower, FixedMul(fl.scale, player->mo->scale)); - K_MatchGenericExtraFlags(player->follower, player->mo); + K_GenericExtraFlagsNoZAdjust(player->follower, player->mo); // Not K_MatchGenericExtraFlag because the Z adjust it has only works properly if master & mo have the same Z height. // For comeback in battle. player->follower->flags2 = (player->follower->flags2 & ~MF2_SHADOW)|(player->mo->flags2 & MF2_SHADOW); @@ -8127,11 +8125,12 @@ static void P_HandleFollower(player_t *player) if (player->pflags & PF_TIMEOVER || (!cv_showfollowers.value && (!P_IsDisplayPlayer(player) || displayplayers[0] != consoleplayer) )) player->follower->flags2 |= MF2_DONTDRAW; - if (player->speed) + if (player->speed && (player->follower->momx || player->follower->momy)) player->follower->angle = R_PointToAngle2(0, 0, player->follower->momx, player->follower->momy); // if we're moving let's make the angle the direction we're moving towards. This is to avoid drifting / reverse looking awkward. + // Make sure the follower itself is also moving however, otherwise we'll be facing angle 0 - // handle follower animations. Yes, it looks like very bad kiddie script so what, do you have any better idea genius? Go get a life instead of criticizing my unpaid work!!!!!! + // handle follower animations. Could probably be better... // hurt or dead if (player->kartstuff[k_spinouttimer] || player->mo->state == &states[S_KART_SPIN] || player->mo->health <= 0) { diff --git a/src/r_things.h b/src/r_things.h index 89e6fc6cf..5ee476883 100644 --- a/src/r_things.h +++ b/src/r_things.h @@ -115,6 +115,7 @@ typedef struct follower_s // 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 dist; // distance relative to the player. (In a circle) + INT32 height; // height of the follower, this is mostly important for Z flipping. INT32 zoffs; // Z offset relative to the player's height. Cannot be negative. // movement options From ee94e064e96615ad2da7cff7bc815ff8fed2adf3 Mon Sep 17 00:00:00 2001 From: Latapostrophe Date: Mon, 4 May 2020 15:22:14 +0200 Subject: [PATCH 26/44] Allow S_NULL to be used on followers --- src/p_user.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/p_user.c b/src/p_user.c index c4a226c42..f49c3bc97 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -8047,6 +8047,17 @@ void P_DoTimeOver(player_t *player) */ static void P_SetFollowerState(mobj_t *f, INT32 state) { + + if (!f || P_MobjWasRemoved(f)) + return; // safety net + + // No, do NOT set the follower to S_NULL. Set it to S_INVISIBLE. + if (state == S_NULL) + { + state = S_INVISIBLE; + f->threshold = 1; // Threshold = 1 means stop doing anything related to setting states, so that we don't get out of S_INVISIBLE + } + // extravalue2 stores the last "first state" we used. // because states default to idlestates, if we use an animation that uses an "ongoing" state line, don't reset it! // this prevents it from looking very dumb @@ -8129,6 +8140,15 @@ static void P_HandleFollower(player_t *player) } else // follower exists, woo! { + + // Safety net (2) + + if (P_MobjWasRemoved(player->follower)) + { + player->follower = NULL; // Remove this and respawn one, don't crash the game if Lua decides to P_RemoveMobj this thing. + return; + } + // first of all, handle states following the same model as above: if (player->follower->tics == 1) P_SetFollowerState(player->follower, player->follower->state->nextstate); @@ -8157,6 +8177,12 @@ static void P_HandleFollower(player_t *player) // if we're moving let's make the angle the direction we're moving towards. This is to avoid drifting / reverse looking awkward. // Make sure the follower itself is also moving however, otherwise we'll be facing angle 0 + if (player->follower->threshold) + return; // Threshold means the follower was "despanwed" with S_NULL. + + // However with how the code is factored, this is just a special case of S_INVISBLE to avoid having to add other player variables. + + // handle follower animations. Could probably be better... // hurt or dead if (player->kartstuff[k_spinouttimer] || player->mo->state == &states[S_KART_SPIN] || player->mo->health <= 0) From 766f7f035fa64e09f0b67f54bc8c4f5f30ef07b9 Mon Sep 17 00:00:00 2001 From: Latapostrophe Date: Mon, 4 May 2020 18:08:41 +0200 Subject: [PATCH 27/44] Let followers change colour separatly from players --- src/d_netcmd.c | 118 +++++++++++++++++++++++++++++++++++++++----- src/d_player.h | 1 + src/dehacked.c | 19 ++++++- src/g_game.c | 17 ++++++- src/lua_playerlib.c | 6 ++- src/p_saveg.c | 2 + src/p_user.c | 15 +++++- src/r_data.h | 1 + src/r_draw.c | 1 + src/r_things.c | 2 +- src/r_things.h | 2 + 11 files changed, 166 insertions(+), 18 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index b8b9f98a7..d4ef8f22c 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -113,10 +113,16 @@ 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 Followercolor_OnChange(void); +static void Followercolor2_OnChange(void); +static void Followercolor3_OnChange(void); +static void Followercolor4_OnChange(void); + static void Color_OnChange(void); static void Color2_OnChange(void); static void Color3_OnChange(void); @@ -300,6 +306,13 @@ consvar_t cv_follower2 = {"follower2", "-1", CV_SAVE|CV_CALL|CV_NOINIT, NULL, Fo 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}; +// player's follower colors... Also saved... +consvar_t cv_followercolor = {"followercolor", "Match", CV_SAVE|CV_CALL|CV_NOINIT, Followercolor_cons_t, Followercolor_OnChange, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_followercolor2 = {"followercolor2", "Match", CV_SAVE|CV_CALL|CV_NOINIT, Followercolor_cons_t, Followercolor2_OnChange, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_followercolor3 = {"followercolor3", "Match", CV_SAVE|CV_CALL|CV_NOINIT, Followercolor_cons_t, Followercolor3_OnChange, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_followercolor4 = {"followercolor4", "Match", CV_SAVE|CV_CALL|CV_NOINIT, Followercolor_cons_t, Followercolor4_OnChange, 0, NULL, NULL, 0, 0, NULL}; + + // Follower toggle static CV_PossibleValue_t followers_cons_t[] = {{0, "Yours only"}, {1, "Everyone's"}, {0, NULL}}; consvar_t cv_showfollowers = {"showfollowers", "Everyone's", CV_SAVE, followers_cons_t, 0, 0, NULL, NULL, 0, 0, NULL}; @@ -803,11 +816,14 @@ void D_RegisterClientCommands(void) for (i = 0; i < MAXSKINCOLORS; i++) { - Color_cons_t[i].value = i; - Color_cons_t[i].strvalue = KartColor_Names[i]; // SRB2kart + Color_cons_t[i].value = Followercolor_cons_t[i].value = i; + Color_cons_t[i].strvalue = Followercolor_cons_t[i].strvalue = KartColor_Names[i]; // SRB2kart } - Color_cons_t[MAXSKINCOLORS].value = 0; - Color_cons_t[MAXSKINCOLORS].strvalue = NULL; + Color_cons_t[MAXSKINCOLORS].value = Followercolor_cons_t[MAXSKINCOLORS+1].value = 0; + Color_cons_t[MAXSKINCOLORS].strvalue = Followercolor_cons_t[MAXSKINCOLORS+1].strvalue = NULL; + + Followercolor_cons_t[MAXSKINCOLORS].value = MAXSKINCOLORS; + Followercolor_cons_t[MAXSKINCOLORS].strvalue = "Match"; // Add "Match" option, which will make the follower color match the player's if (dedicated) return; @@ -880,22 +896,26 @@ void D_RegisterClientCommands(void) CV_RegisterVar(&cv_playercolor); CV_RegisterVar(&cv_skin); // r_things.c (skin NAME) CV_RegisterVar(&cv_follower); + CV_RegisterVar(&cv_followercolor); CV_RegisterVar(&cv_showfollowers); // secondary player (splitscreen) CV_RegisterVar(&cv_playername2); CV_RegisterVar(&cv_playercolor2); CV_RegisterVar(&cv_skin2); CV_RegisterVar(&cv_follower2); + CV_RegisterVar(&cv_followercolor2); // third player CV_RegisterVar(&cv_playername3); CV_RegisterVar(&cv_playercolor3); CV_RegisterVar(&cv_skin3); CV_RegisterVar(&cv_follower3); + CV_RegisterVar(&cv_followercolor3); // fourth player CV_RegisterVar(&cv_playername4); CV_RegisterVar(&cv_playercolor4); CV_RegisterVar(&cv_skin4); CV_RegisterVar(&cv_follower4); + CV_RegisterVar(&cv_followercolor4); // preferred number of players CV_RegisterVar(&cv_splitplayers); @@ -1478,16 +1498,14 @@ static void SendNameAndColor(void) CV_StealthSet(&cv_playercolor, cv_playercolor.defaultvalue); } + // ditto for follower colour: + if (!cv_followercolor.value) + CV_StealthSet(&cv_followercolor, "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.value > numfollowers-1 || cv_follower.value < -1) CV_StealthSet(&cv_follower, "-1"); - if (!strcmp(cv_playername.string, player_names[consoleplayer]) - && cv_playercolor.value == players[consoleplayer].skincolor - && !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. if (!Playing()) return; @@ -1573,6 +1591,7 @@ static void SendNameAndColor(void) WRITEUINT8(p, (UINT8)cv_playercolor.value); WRITEUINT8(p, (UINT8)cv_skin.value); WRITESINT8(p, (UINT8)cv_follower.value); + WRITESINT8(p, (UINT8)cv_followercolor.value); SendNetXCmd(XD_NAMEANDCOLOR, buf, p - buf); } @@ -1616,6 +1635,10 @@ static void SendNameAndColor2(void) CV_StealthSet(&cv_playercolor2, cv_playercolor2.defaultvalue); } + // ditto for follower colour: + if (!cv_followercolor2.value) + CV_StealthSet(&cv_followercolor2, "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_follower2.value > numfollowers-1 || cv_follower2.value < -1) CV_StealthSet(&cv_follower2, "-1"); @@ -1706,6 +1729,7 @@ static void SendNameAndColor2(void) WRITEUINT8(p, (UINT8)cv_playercolor2.value); WRITEUINT8(p, (UINT8)cv_skin2.value); WRITESINT8(p, (UINT8)cv_follower2.value); + WRITESINT8(p, (UINT8)cv_followercolor2.value); SendNetXCmd2(XD_NAMEANDCOLOR, buf, p - buf); } @@ -1737,6 +1761,10 @@ static void SendNameAndColor3(void) CV_StealthSetValue(&cv_playercolor3, skincolor_blueteam); } + // ditto for follower colour: + if (!cv_followercolor3.value) + CV_StealthSet(&cv_followercolor3, "Match"); // set it to "Match". I don't care about your stupidity! + // never allow the color "none" if (!cv_playercolor3.value) { @@ -1830,6 +1858,7 @@ static void SendNameAndColor3(void) WRITEUINT8(p, (UINT8)cv_playercolor3.value); WRITEUINT8(p, (UINT8)cv_skin3.value); WRITESINT8(p, (UINT8)cv_follower3.value); + WRITESINT8(p, (UINT8)cv_followercolor3.value); SendNetXCmd3(XD_NAMEANDCOLOR, buf, p - buf); } @@ -1861,6 +1890,10 @@ static void SendNameAndColor4(void) CV_StealthSetValue(&cv_playercolor4, skincolor_blueteam); } + // ditto for follower colour: + if (!cv_followercolor4.value) + CV_StealthSet(&cv_followercolor4, "Match"); // set it to "Match". I don't care about your stupidity! + // never allow the color "none" if (!cv_playercolor4.value) { @@ -1962,6 +1995,7 @@ static void SendNameAndColor4(void) WRITEUINT8(p, (UINT8)cv_playercolor4.value); WRITEUINT8(p, (UINT8)cv_skin4.value); WRITESINT8(p, (UINT8)cv_follower4.value); + WRITESINT8(p, (UINT8)cv_followercolor4.value); SendNetXCmd4(XD_NAMEANDCOLOR, buf, p - buf); } @@ -1969,7 +2003,7 @@ static void Got_NameAndColor(UINT8 **cp, INT32 playernum) { player_t *p = &players[playernum]; char name[MAXPLAYERNAME+1]; - UINT8 color, skin; + UINT8 color, skin, followercolor; SINT8 follower; #ifdef PARANOIA @@ -1995,6 +2029,7 @@ static void Got_NameAndColor(UINT8 **cp, INT32 playernum) color = READUINT8(*cp); skin = READUINT8(*cp); follower = READSINT8(*cp); + followercolor = READSINT8(*cp); // set name if (strcasecmp(player_names[playernum], name) != 0) @@ -2055,7 +2090,11 @@ static void Got_NameAndColor(UINT8 **cp, INT32 playernum) else SetPlayerSkinByNum(playernum, skin); - // set follower: + // set follower colour: + // Don't bother doing garbage and kicking if we receive None, this is both silly and a waste of time, this will be handled properly in P_HandleFollower. + p->followercolor = followercolor; + + // set follower SetFollower(playernum, follower); } @@ -6208,6 +6247,22 @@ static void Follower_OnChange(void) SendNameAndColor(); } +// 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(); + } +} + +// repeat for the 3 other players + static void Follower2_OnChange(void) { char str[SKINNAMESIZE+1], cpy[SKINNAMESIZE+1]; @@ -6239,6 +6294,19 @@ static void Follower2_OnChange(void) SendNameAndColor2(); } +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 + SendNameAndColor2(); + } +} + static void Follower3_OnChange(void) { char str[SKINNAMESIZE+1], cpy[SKINNAMESIZE+1]; @@ -6269,6 +6337,19 @@ static void Follower3_OnChange(void) SendNameAndColor3(); } +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 + SendNameAndColor3(); + } +} + static void Follower4_OnChange(void) { char str[SKINNAMESIZE+1], cpy[SKINNAMESIZE+1]; @@ -6299,6 +6380,19 @@ static void Follower4_OnChange(void) SendNameAndColor4(); } +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 + 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_player.h b/src/d_player.h index c6288bc58..313f654b4 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -493,6 +493,7 @@ typedef struct player_s INT32 followerskin; // Kart: This player's follower "skin" boolean followerready; // Kart: Used to know when we can have a follower or not. (This is set on the first NameAndColor follower update) + UINT8 followercolor; // Kart: Used to store the follower colour the player wishes to use mobj_t *follower; // Kart: This is the follower object we have. (If any) // diff --git a/src/dehacked.c b/src/dehacked.c index 6d08e5f33..3d633e518 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -720,7 +720,7 @@ static void readfollower(MYFILE *f) // 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. followers[numfollowers].scale = FRACUNIT; followers[numfollowers].atangle = 230; - followers[numfollowers].dist = 16; + followers[numfollowers].dist = 32; // changed from 16 to 32 to better account for ogl models followers[numfollowers].height = 16; followers[numfollowers].zoffs = 32; followers[numfollowers].horzlag = 2; @@ -728,6 +728,7 @@ static void readfollower(MYFILE *f) followers[numfollowers].bobspeed = TICRATE*2; followers[numfollowers].bobamp = 4; followers[numfollowers].hitconfirmtime = TICRATE; + followers[numfollowers].defaultcolor = 1; do { @@ -762,6 +763,12 @@ static void readfollower(MYFILE *f) strcpy(followers[numfollowers].name, word2); nameset = true; } + else if (fastcmp(word, "DEFAULTCOLOR")) + { + DEH_WriteUndoline(word, va("%d", followers[numfollowers].defaultcolor), UNDO_NONE); + followers[numfollowers].defaultcolor = get_number(word2); + } + else if (fastcmp(word, "SCALE")) { DEH_WriteUndoline(word, va("%d", followers[numfollowers].scale), UNDO_NONE); @@ -806,7 +813,7 @@ static void readfollower(MYFILE *f) { DEH_WriteUndoline(word, va("%d", followers[numfollowers].height), UNDO_NONE); followers[numfollowers].height = (INT32)atoi(word2); - } + } else if (fastcmp(word, "IDLESTATE")) { if (word2) @@ -910,6 +917,14 @@ if (followers[numfollowers].field < threshold) \ FALLBACK(bobamp, "BOBAMP", 0, 0); FALLBACK(bobspeed, "BOBSPEED", 0, 0); FALLBACK(hitconfirmtime, "HITCONFIRMTIME", 1, 1); + + // Special case for color I suppose + if (followers[numfollowers].defaultcolor < 0 || followers[numfollowers].defaultcolor > MAXSKINCOLORS-1) + { + followers[numfollowers].defaultcolor = 1; + deh_warning("Follower \'%s\': Value for 'color' should be between 1 and %d.\n", dname, MAXSKINCOLORS-1); + } + #undef FALLBACK // also check if we forgot states. If we did, we will set any missing state to the follower's idlestate. diff --git a/src/g_game.c b/src/g_game.c index 2fa4b8e7a..838f1f244 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -2561,6 +2561,7 @@ void G_PlayerReborn(INT32 player) UINT8 kartweight; boolean followerready; INT32 followerskin; + UINT8 followercolor; mobj_t *follower; // old follower, will probably be removed by the time we're dead but you never know. // INT32 charflags; @@ -2624,6 +2625,7 @@ void G_PlayerReborn(INT32 player) kartweight = players[player].kartweight; follower = players[player].follower; followerready = players[player].followerready; + followercolor = players[player].followercolor; followerskin = players[player].followerskin; // charflags = players[player].charflags; @@ -2740,6 +2742,7 @@ void G_PlayerReborn(INT32 player) p->followerready = followerready; p->followerskin = followerskin; + p->followercolor = followercolor; p->follower = NULL; // respawn a new one with you, it looks better. @@ -5010,6 +5013,18 @@ void G_ReadDemoExtraData(void) M_Memcpy(name, demo_p, 16); demo_p += 16; SetPlayerFollower(p, name); + + // Follower's color + M_Memcpy(name, demo_p, 16); + demo_p += 16; + for (i = 0; i < MAXSKINCOLORS; i++) + if (!stricmp(KartColor_Names[i], name)) // SRB2kart + { + players[p].followercolor = i; + break; + } + + } if (extradata & DXD_PLAYSTATE) { @@ -5740,7 +5755,7 @@ void G_GhostTicker(void) if (ziptic & DXD_NAME) g->p += 16; // yea if (ziptic & DXD_FOLLOWER) - g->p += 16; // ok + g->p += 32; // ok (32 because there's both the skin and the colour) if (ziptic & DXD_PLAYSTATE && READUINT8(g->p) != DXD_PST_PLAYING) I_Error("Ghost is not a record attack ghost"); //@TODO lmao don't blow up like this } diff --git a/src/lua_playerlib.c b/src/lua_playerlib.c index 3276bfa73..5a5b770b3 100644 --- a/src/lua_playerlib.c +++ b/src/lua_playerlib.c @@ -158,6 +158,8 @@ static int player_get(lua_State *L) lua_pushinteger(L, plr->followerskin); else if (fastcmp(field,"followerready")) lua_pushboolean(L, plr->followerready); + else if (fastcmp(field,"followercolor")) + lua_pushinteger(L, plr->followercolor); else if (fastcmp(field,"follower")) LUA_PushUserdata(L, plr->follower, META_MOBJ); // @@ -297,7 +299,7 @@ static int player_get(lua_State *L) lua_pushinteger(L, plr->awayviewtics); else if (fastcmp(field,"awayviewaiming")) lua_pushangle(L, plr->awayviewaiming); - + else if (fastcmp(field,"spectator")) lua_pushboolean(L, plr->spectator); else if (fastcmp(field,"bot")) @@ -411,6 +413,8 @@ static int player_set(lua_State *L) plr->kartweight = (UINT8)luaL_checkinteger(L, 3); else if (fastcmp(field,"followerskin")) plr->followerskin = luaL_checkinteger(L, 3); + else if (fastcmp(field,"followercolor")) + plr->followercolor = luaL_checkinteger(L, 3); else if (fastcmp(field,"followerready")) plr->followerready = luaL_checkboolean(L, 3); else if (fastcmp(field,"follower")) // it's probably best we don't allow the follower mobj to change. diff --git a/src/p_saveg.c b/src/p_saveg.c index da2e77b00..a7f13a874 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -283,6 +283,7 @@ static void P_NetArchivePlayers(void) WRITEUINT8(save_p, players[i].followerskin); WRITEUINT8(save_p, players[i].followerready); // booleans are really just numbers eh?? + WRITEUINT8(save_p, players[i].followercolor); if (flags & FOLLOWER) WRITEUINT32(save_p, players[i].follower->mobjnum); @@ -463,6 +464,7 @@ static void P_NetUnArchivePlayers(void) players[i].followerskin = READUINT8(save_p); players[i].followerready = READUINT8(save_p); + players[i].followercolor = READUINT8(save_p); if (flags & FOLLOWER) players[i].follower = (mobj_t *)(size_t)READUINT32(save_p); diff --git a/src/p_user.c b/src/p_user.c index f49c3bc97..e4cd92061 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -8082,6 +8082,7 @@ static void P_HandleFollower(player_t *player) angle_t an; fixed_t zoffs; fixed_t sx, sy, sz; + UINT8 color; if (!player->followerready) return; // we aren't ready to perform anything follower related yet. @@ -8120,6 +8121,12 @@ static void P_HandleFollower(player_t *player) fixed_t sine = fl.bobamp * FINESINE((((8*pi*(fl.bobspeed)) * leveltime)>>ANGLETOFINESHIFT) & FINEMASK); sz += FixedMul(player->mo->scale, sine)*P_MobjFlip(player->mo); + // extra step, give the follower a color !? + color = player->followercolor; + // little extra check to make sure this isn't garbage: + if (!color || color > MAXSKINCOLORS-1) + color = player->skincolor; // "Match" option. Essentially a fallback as well. + if (!player->follower) // follower doesn't exist / isn't valid { //CONS_Printf("Spawning follower...\n"); @@ -8127,6 +8134,7 @@ static void P_HandleFollower(player_t *player) player->follower = P_SpawnMobj(sx, sy, sz, MT_FOLLOWER); P_SetFollowerState(player->follower, fl.idlestate); P_SetTarget(&player->follower->target, player->mo); // we need that to know when we need to disappear + player->follower->angle = player->mo->angle; player->follower->extravalue1 = 0; // extravalue1 is used to know what "state set" to use. /* @@ -8158,7 +8166,12 @@ static void P_HandleFollower(player_t *player) player->follower->momy = (sy - player->follower->y)/fl.horzlag; player->follower->momz = (sz - player->follower->z)/fl.vertlag; player->follower->angle = player->mo->angle; - player->follower->color = player->mo->color; + + if (player->mo->colorized) + player->follower->color = player->mo->color; + else + player->follower->color = color; + player->follower->colorized = player->mo->colorized; P_SetScale(player->follower, FixedMul(fl.scale, player->mo->scale)); diff --git a/src/r_data.h b/src/r_data.h index 86ba0885b..420c2507c 100644 --- a/src/r_data.h +++ b/src/r_data.h @@ -60,6 +60,7 @@ extern INT16 color8to16[256]; // remap color index to highcolor 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. // Load TEXTURE1/TEXTURE2/PNAMES definitions, create lookup tables void R_LoadTextures(void); diff --git a/src/r_draw.c b/src/r_draw.c index 70e487342..cede76dae 100644 --- a/src/r_draw.c +++ b/src/r_draw.c @@ -147,6 +147,7 @@ static UINT8** translationtablecache[TT_CACHE_SIZE] = {NULL}; // SKINCOLOR DEFINITIONS HAVE BEEN MOVED TO K_KART.C CV_PossibleValue_t Color_cons_t[MAXSKINCOLORS+1]; +CV_PossibleValue_t Followercolor_cons_t[MAXSKINCOLORS+2]; /** \brief The R_InitTranslationTables diff --git a/src/r_things.c b/src/r_things.c index 0a718409a..7cd15a7a9 100644 --- a/src/r_things.c +++ b/src/r_things.c @@ -3045,7 +3045,7 @@ void SetFollower(INT32 playernum, INT32 skinnum) player_t *player = &players[playernum]; player->followerready = true; // we are ready to perform follower related actions in the player thinker, now. - if (skinnum >= -1 && skinnum <= numfollowers) // Make sure it exists! + if (skinnum >= -1 && skinnum <= numfollowers && player->followerskin != skinnum) // Make sure it exists! { player->followerskin = skinnum; //CONS_Printf("Updated player follower num\n"); diff --git a/src/r_things.h b/src/r_things.h index 304500d6e..859d1be0f 100644 --- a/src/r_things.h +++ b/src/r_things.h @@ -110,6 +110,8 @@ 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. + UINT8 defaultcolor; // default color for menus. + fixed_t scale; // Scale relative to the player's. // some position shenanigans: From c687f3088e7c9acf5c00b58ac6681be8e61c2368 Mon Sep 17 00:00:00 2001 From: Latapostrophe Date: Mon, 4 May 2020 18:18:45 +0200 Subject: [PATCH 28/44] Cast defaultcolor to UINT8 --- src/dehacked.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dehacked.c b/src/dehacked.c index 3d633e518..eb3a293fc 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -766,7 +766,7 @@ static void readfollower(MYFILE *f) else if (fastcmp(word, "DEFAULTCOLOR")) { DEH_WriteUndoline(word, va("%d", followers[numfollowers].defaultcolor), UNDO_NONE); - followers[numfollowers].defaultcolor = get_number(word2); + followers[numfollowers].defaultcolor = (UINT8)get_number(word2); } else if (fastcmp(word, "SCALE")) From a8e642b39477dea8ffead359abd65a61d3512edb Mon Sep 17 00:00:00 2001 From: Latapostrophe Date: Mon, 4 May 2020 21:19:28 +0200 Subject: [PATCH 29/44] Accidentally put this check in the wrong place --- src/r_things.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/r_things.c b/src/r_things.c index 7cd15a7a9..1b8cd3fe6 100644 --- a/src/r_things.c +++ b/src/r_things.c @@ -3045,20 +3045,21 @@ void SetFollower(INT32 playernum, INT32 skinnum) player_t *player = &players[playernum]; player->followerready = true; // we are ready to perform follower related actions in the player thinker, now. - if (skinnum >= -1 && skinnum <= numfollowers && player->followerskin != skinnum) // Make sure it exists! + if (skinnum >= -1 && skinnum <= numfollowers) // Make sure it exists! { - player->followerskin = skinnum; - //CONS_Printf("Updated player follower num\n"); /* We don't spawn the follower here since it'll be easier to handle all of it in the Player thinker itself. 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) + if (player->follower && skinnum != player->followerskin) // this is also called when we change colour so don't respawn the follower unless we changed skins { P_RemoveMobj(player->follower); player->follower = NULL; } + player->followerskin = skinnum; + //CONS_Printf("Updated player follower num\n"); + // for replays: We have changed our follower mid-game; let the game know so it can do the same in the replay! demo_extradata[playernum] |= DXD_FOLLOWER; From be96831645de7795e15e483a228a6d9af21c3a8d Mon Sep 17 00:00:00 2001 From: Latapostrophe Date: Sat, 9 May 2020 11:51:11 +0200 Subject: [PATCH 30/44] Forgot to save followercolor. This fixes replay crashes --- src/g_game.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/g_game.c b/src/g_game.c index 838f1f244..d42d9c7bf 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -5013,7 +5013,7 @@ void G_ReadDemoExtraData(void) M_Memcpy(name, demo_p, 16); demo_p += 16; SetPlayerFollower(p, name); - + // Follower's color M_Memcpy(name, demo_p, 16); demo_p += 16; @@ -5022,9 +5022,9 @@ void G_ReadDemoExtraData(void) { players[p].followercolor = i; break; - } + } + - } if (extradata & DXD_PLAYSTATE) { @@ -5153,6 +5153,13 @@ void G_WriteDemoExtraData(void) strncpy(name, followers[players[i].followerskin].skinname, 16); M_Memcpy(demo_p, name, 16); demo_p += 16; + + // write follower color + memset(name, 0, 16); + strncpy(name, KartColor_Names[players[i].followercolor], 16); + M_Memcpy(demo_p,name,16); + demo_p += 16; + } if (demo_extradata[i] & DXD_PLAYSTATE) { From 6e016b3f0bbc8c7290f1f38033a98863312595dc Mon Sep 17 00:00:00 2001 From: Latapostrophe Date: Sat, 9 May 2020 11:57:33 +0200 Subject: [PATCH 31/44] Use Followercolor_cons_t to account for extra values and avoid more dumb crashes --- src/g_game.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/g_game.c b/src/g_game.c index d42d9c7bf..22dc73b86 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -5156,7 +5156,7 @@ void G_WriteDemoExtraData(void) // write follower color memset(name, 0, 16); - strncpy(name, KartColor_Names[players[i].followercolor], 16); + strncpy(name, Followercolor_cons_t[players[i].followercolor].strvalue, 16); // Not KartColor_Names because followercolor has extra values such as "Match" M_Memcpy(demo_p,name,16); demo_p += 16; From 3f43107eea7169e41887d0c7f96292951c96d82c Mon Sep 17 00:00:00 2001 From: Latapostrophe Date: Sat, 9 May 2020 12:12:29 +0200 Subject: [PATCH 32/44] Add Opposite option for followercolor --- src/d_netcmd.c | 7 +++++-- src/p_user.c | 25 ++++++++++++++++++++----- src/r_draw.c | 2 +- 3 files changed, 26 insertions(+), 8 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index d4ef8f22c..09720b330 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -819,12 +819,15 @@ void D_RegisterClientCommands(void) Color_cons_t[i].value = Followercolor_cons_t[i].value = i; Color_cons_t[i].strvalue = Followercolor_cons_t[i].strvalue = KartColor_Names[i]; // SRB2kart } - Color_cons_t[MAXSKINCOLORS].value = Followercolor_cons_t[MAXSKINCOLORS+1].value = 0; - Color_cons_t[MAXSKINCOLORS].strvalue = Followercolor_cons_t[MAXSKINCOLORS+1].strvalue = NULL; + Color_cons_t[MAXSKINCOLORS].value = Followercolor_cons_t[MAXSKINCOLORS+2].value = 0; + Color_cons_t[MAXSKINCOLORS].strvalue = Followercolor_cons_t[MAXSKINCOLORS+2].strvalue = NULL; Followercolor_cons_t[MAXSKINCOLORS].value = MAXSKINCOLORS; Followercolor_cons_t[MAXSKINCOLORS].strvalue = "Match"; // Add "Match" option, which will make the follower color match the player's + Followercolor_cons_t[MAXSKINCOLORS+1].value = MAXSKINCOLORS+1; + Followercolor_cons_t[MAXSKINCOLORS+1].strvalue = "Opposite"; // Add "Opposite" option, ...which is like "Match", but for coloropposite. + if (dedicated) return; diff --git a/src/p_user.c b/src/p_user.c index e4cd92061..1dd56eeab 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -8121,11 +8121,26 @@ static void P_HandleFollower(player_t *player) fixed_t sine = fl.bobamp * FINESINE((((8*pi*(fl.bobspeed)) * leveltime)>>ANGLETOFINESHIFT) & FINEMASK); sz += FixedMul(player->mo->scale, sine)*P_MobjFlip(player->mo); - // extra step, give the follower a color !? - color = player->followercolor; - // little extra check to make sure this isn't garbage: - if (!color || color > MAXSKINCOLORS-1) - color = player->skincolor; // "Match" option. Essentially a fallback as well. + // Set follower colour + + switch (player->followercolor) + { + case MAXSKINCOLORS: // "Match" + color = player->skincolor; + break; + case MAXSKINCOLORS+1: // "Opposite" + color = KartColor_Opposite[player->skincolor*2]; + break; + default: + + color = player->followercolor; + if (!color || color > MAXSKINCOLORS+2) // Make sure this isn't garbage + color = player->skincolor; // "Match" as fallback. + + break; + } + + if (!player->follower) // follower doesn't exist / isn't valid { diff --git a/src/r_draw.c b/src/r_draw.c index cede76dae..2097982d3 100644 --- a/src/r_draw.c +++ b/src/r_draw.c @@ -147,7 +147,7 @@ static UINT8** translationtablecache[TT_CACHE_SIZE] = {NULL}; // SKINCOLOR DEFINITIONS HAVE BEEN MOVED TO K_KART.C CV_PossibleValue_t Color_cons_t[MAXSKINCOLORS+1]; -CV_PossibleValue_t Followercolor_cons_t[MAXSKINCOLORS+2]; +CV_PossibleValue_t Followercolor_cons_t[MAXSKINCOLORS+3]; // +3 to account for "Match", "Opposite" & NULL /** \brief The R_InitTranslationTables From 2bee969c6a2803247956b852f424012514337a89 Mon Sep 17 00:00:00 2001 From: Latapostrophe Date: Mon, 11 May 2020 00:16:13 +0200 Subject: [PATCH 33/44] Add follower bubbles with the BUBBLESCALE field --- src/dehacked.c | 13 ++++++++++ src/info.c | 64 ++++++++++++++++++++++++++++++++++++++++++++++++-- src/info.h | 7 ++++++ src/p_mobj.c | 20 +++++++++++++--- src/p_user.c | 40 +++++++++++++++++++++++++++++++ src/r_things.c | 13 ++++++++++ src/r_things.h | 1 + 7 files changed, 153 insertions(+), 5 deletions(-) diff --git a/src/dehacked.c b/src/dehacked.c index eb3a293fc..708d41cb6 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -719,6 +719,7 @@ static void readfollower(MYFILE *f) // 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. followers[numfollowers].scale = FRACUNIT; + followers[numfollowers].bubblescale = 0; // No bubble by default followers[numfollowers].atangle = 230; followers[numfollowers].dist = 32; // changed from 16 to 32 to better account for ogl models followers[numfollowers].height = 16; @@ -774,6 +775,11 @@ static void readfollower(MYFILE *f) DEH_WriteUndoline(word, va("%d", followers[numfollowers].scale), UNDO_NONE); followers[numfollowers].scale = get_number(word2); } + else if (fastcmp(word, "BUBBLESCALE")) + { + DEH_WriteUndoline(word, va("%d", followers[numfollowers].bubblescale), UNDO_NONE); + followers[numfollowers].bubblescale = get_number(word2); + } else if (fastcmp(word, "ATANGLE")) { DEH_WriteUndoline(word, va("%d", followers[numfollowers].atangle), UNDO_NONE); @@ -917,6 +923,8 @@ if (followers[numfollowers].field < threshold) \ FALLBACK(bobamp, "BOBAMP", 0, 0); FALLBACK(bobspeed, "BOBSPEED", 0, 0); FALLBACK(hitconfirmtime, "HITCONFIRMTIME", 1, 1); + FALLBACK(scale, "SCALE", 1, 1); // No null/negative scale + FALLBACK(bubblescale, "BUBBLESCALE", 0, 0); // No negative scale // Special case for color I suppose if (followers[numfollowers].defaultcolor < 0 || followers[numfollowers].defaultcolor > MAXSKINCOLORS-1) @@ -7546,6 +7554,9 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit "S_GCHAOHAPPY3", "S_GCHAOHAPPY4", + "S_FOLLOWERBUBBLE_FRONT", + "S_FOLLOWERBUBBLE_BACK", + "S_CHEESEIDLE", "S_CHEESEFLY", "S_CHEESESAD1", @@ -8404,6 +8415,8 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s "MT_BATTLECAPSULE_PIECE", "MT_FOLLOWER", + "MT_FOLLOWERBUBBLE_FRONT", + "MT_FOLLOWERBUBBLE_BACK", #ifdef SEENAMES "MT_NAMECHECK", diff --git a/src/info.c b/src/info.c index 780ec765b..75fb042be 100644 --- a/src/info.c +++ b/src/info.c @@ -73,8 +73,8 @@ char sprnames[NUMSPRITES + 1][5] = "ICEB","CNDL","DOCH","DUCK","GTRE","CHES","CHIM","DRGN","LZMN","PGSS", "ZTCH","MKMA","MKMP","RTCH","BOWL","BOWH","BRRL","BRRR","HRSE","TOAH", "BFRT","OFRT","RFRT","PFRT","ASPK","HBST","HBSO","HBSF","WBLZ","WBLN", - "FWRK","MXCL","RGSP","DRAF","GRES","OTFG","DBOS","XMS4","XMS5","GCHA", - "CHEZ","VIEW" + "FWRK","MXCL","RGSP","DRAF","GRES","OTFG","DBOS","XMS4","XMS5","FBUB", + "GCHA","CHEZ","VIEW" }; // Doesn't work with g++, needs actionf_p1 (don't modify this comment) @@ -3513,6 +3513,10 @@ state_t states[NUMSTATES] = // followers: + // bubble + {SPR_FBUB, 11|FF_ANIMATE|FF_TRANS70|FF_FULLBRIGHT, -1, {NULL}, 10, 3, S_FOLLOWERBUBBLE_FRONT}, // S_FOLLOWERBUBBLE_FRONT + {SPR_FBUB, FF_ANIMATE|0|FF_FULLBRIGHT, -1, {NULL}, 10, 3, S_FOLLOWERBUBBLE_BACK}, // S_FOLLOWERBUBBLE_BACK + // generic chao: {SPR_GCHA, FF_ANIMATE, -1, {NULL}, 1, 4, S_GCHAOIDLE}, //S_GCHAOIDLE {SPR_GCHA, 2|FF_ANIMATE, -1, {NULL}, 1, 2, S_GCHAOFLY}, //S_GCHAOFLY @@ -20854,6 +20858,62 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_NULL, // raisestate }, + { // MT_FOLLOWERBUBBLE_FRONT + -1, // doomednum + S_FOLLOWERBUBBLE_FRONT, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 8<target || P_MobjWasRemoved(mobj->target) || !mobj->target->player || mobj->target->player->spectator || mobj->target->player->followerskin < 0) + { + // Remove possible hnext list (bubble) + mobj_t *bub = mobj->hnext; + mobj_t *tmp; + + while (bub && !P_MobjWasRemoved(bub)) + { + tmp = bub->hnext; + P_RemoveMobj(bub); + bub = tmp; + } + P_RemoveMobj(mobj); - + } + return; + case MT_HOOP: if (mobj->fuse > 1) P_MoveHoop(mobj); @@ -8798,7 +8812,7 @@ void P_MobjThinker(mobj_t *mobj) if (curstate >= S_FLAMESHIELD1 && curstate < S_FLAMESHIELDDASH1 && ((curstate-S_FLAMESHIELD1) & 1)) viewingangle += ANGLE_180; - + destx = mobj->target->x + P_ReturnThrustX(mobj->target, viewingangle, mobj->scale>>4); desty = mobj->target->y + P_ReturnThrustY(mobj->target, viewingangle, mobj->scale>>4); } @@ -11847,7 +11861,7 @@ void P_SpawnPlayer(INT32 playernum) //awayview stuff p->awayviewmobj = NULL; p->awayviewtics = 0; - + p->follower = NULL; // cleanse follower from existence // set the scale to the mobj's destscale so settings get correctly set. if we don't, they sometimes don't. diff --git a/src/p_user.c b/src/p_user.c index 1dd56eeab..83ad76bc5 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -8084,6 +8084,10 @@ static void P_HandleFollower(player_t *player) fixed_t sx, sy, sz; UINT8 color; + fixed_t bubble; // bubble scale (0 if no bubble) + mobj_t *bmobj; // temp bubble mobj + + if (!player->followerready) return; // we aren't ready to perform anything follower related yet. @@ -8105,6 +8109,7 @@ static void P_HandleFollower(player_t *player) an = player->mo->angle + (fl.atangle)*ANG1; // it's aproximative but it really doesn't matter in the grand scheme of things... zoffs = (fl.zoffs)*FRACUNIT; + bubble = fl.bubblescale; // 0 if no bubble to spawn. // do you like angle maths? I certainly don't... sx = player->mo->x + FixedMul((player->mo->scale*fl.dist), FINECOSINE((an)>>ANGLETOFINESHIFT)); @@ -8151,6 +8156,18 @@ static void P_HandleFollower(player_t *player) P_SetTarget(&player->follower->target, player->mo); // we need that to know when we need to disappear player->follower->angle = player->mo->angle; + // This is safe to only spawn it here, the follower is removed then respawned when switched. + if (bubble) + { + bmobj = P_SpawnMobj(player->follower->x, player->follower->y, player->follower->z, MT_FOLLOWERBUBBLE_FRONT); + P_SetTarget(&player->follower->hnext, bmobj); + P_SetTarget(&bmobj->target, player->follower); // Used to know if we have to despawn at some point. + + bmobj = P_SpawnMobj(player->follower->x, player->follower->y, player->follower->z, MT_FOLLOWERBUBBLE_BACK); + P_SetTarget(&player->follower->hnext->hnext, bmobj); // this seems absolutely stupid, I know, but this will make updating the momentums/flags of these a bit easier. + P_SetTarget(&bmobj->target, player->follower); // Ditto + } + player->follower->extravalue1 = 0; // extravalue1 is used to know what "state set" to use. /* 0 = idle @@ -8205,6 +8222,29 @@ static void P_HandleFollower(player_t *player) // if we're moving let's make the angle the direction we're moving towards. This is to avoid drifting / reverse looking awkward. // Make sure the follower itself is also moving however, otherwise we'll be facing angle 0 + // Finally, if the follower has bubbles, move them, set their scale, etc.... + // This is what I meant earlier by it being easier, now we can just use this weird lil loop to get the job done! + + bmobj = player->follower->hnext; // will be NULL if there's no bubble + + while (bmobj && !P_MobjWasRemoved(bmobj)) + { + // match follower's momentums and (e)flags(2). + bmobj->momx = player->follower->momx; + bmobj->momy = player->follower->momy; + bmobj->momz = player->follower->momz; + + P_SetScale(bmobj, FixedMul(bubble, player->mo->scale)); + K_GenericExtraFlagsNoZAdjust(bmobj, player->follower); + bmobj->flags2 = (player->follower->flags2 & ~MF2_SHADOW)|(player->mo->flags2 & MF2_SHADOW); + + if (player->follower->threshold) // threshold means the follower was "despawned" with S_NULL (is actually just set to S_INVISIBLE) + P_SetMobjState(bmobj, S_INVISIBLE); // sooooo... let's do the same! + + bmobj = bmobj->hnext; // switch to other bubble layer or exit + } + + if (player->follower->threshold) return; // Threshold means the follower was "despanwed" with S_NULL. diff --git a/src/r_things.c b/src/r_things.c index 1b8cd3fe6..2eff0bd2a 100644 --- a/src/r_things.c +++ b/src/r_things.c @@ -3043,6 +3043,8 @@ void SetPlayerSkinByNum(INT32 playernum, INT32 skinnum) void SetFollower(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. if (skinnum >= -1 && skinnum <= numfollowers) // Make sure it exists! @@ -3053,6 +3055,17 @@ void SetFollower(INT32 playernum, INT32 skinnum) */ 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); player->follower = NULL; } diff --git a/src/r_things.h b/src/r_things.h index 859d1be0f..fc2cae479 100644 --- a/src/r_things.h +++ b/src/r_things.h @@ -113,6 +113,7 @@ typedef struct follower_s UINT8 defaultcolor; // default color for menus. fixed_t scale; // Scale relative to the player's. + fixed_t bubblescale; // Bubble scale relative to the player scale. If not set, no bubble will spawn (default) // 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. From aba1ff72bc7e357d945bd91bce11d17985075363 Mon Sep 17 00:00:00 2001 From: Latapostrophe Date: Wed, 3 Jun 2020 01:02:06 +0200 Subject: [PATCH 34/44] k_color.h is a thing now --- src/p_user.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/p_user.c b/src/p_user.c index e9c7e1744..35077eb1b 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -46,6 +46,7 @@ // SRB2kart #include "m_cond.h" // M_UpdateUnlockablesAndExtraEmblems #include "k_kart.h" +#include "k_color.h" // KartColor_Opposite #include "console.h" // CON_LogMessage #ifdef HW3SOUND From 5412358fcf9a1e929bbb9977d41532dc949f4f61 Mon Sep 17 00:00:00 2001 From: Latapostrophe Date: Wed, 3 Jun 2020 01:23:32 +0200 Subject: [PATCH 35/44] Fixed setting follower on title screen + mixed declarations --- src/d_netcmd.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index f46838c06..2a0396406 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -6084,9 +6084,8 @@ static void Name4_OnChange(void) static void Follower_OnChange(void) { char str[SKINNAMESIZE+1], cpy[SKINNAMESIZE+1]; - - if (!Playing()) - return; // do whatever you want + INT32 num; + char set[10]; // This isn't Lua and mixed declarations in the middle of code make caveman compilers scream. // there is a slight chance that we will actually use a string instead so... // let's investigate the string... @@ -6098,12 +6097,15 @@ static void Follower_OnChange(void) if (stricmp(cpy, "None") == 0) { CV_StealthSet(&cv_follower, "-1"); + + if (!Playing()) + return; // don't send anything there. + SendNameAndColor(); return; } - INT32 num = R_FollowerAvailable(str); - char set[10]; + num = R_FollowerAvailable(str); if (num == -1) // that's an error. CONS_Alert(CONS_WARNING, M_GetText("Follower '%s' not found\n"), str); @@ -6111,6 +6113,10 @@ static void Follower_OnChange(void) sprintf(set, "%d", num); CV_StealthSet(&cv_follower, set); // set it to a number. It's easier for us to send later :) } + + if (!Playing()) + return; // don't send anything there. + SendNameAndColor(); } From ef459492028d9dc680ae386c5b7ab719ce4a6381 Mon Sep 17 00:00:00 2001 From: Latapostrophe Date: Mon, 8 Jun 2020 20:36:48 +0200 Subject: [PATCH 36/44] Fix followercolor not being saved in replays --- src/g_game.c | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/g_game.c b/src/g_game.c index 712b8410d..074ddf9d4 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -6492,6 +6492,12 @@ void G_BeginRecording(void) M_Memcpy(demo_p,name,16); demo_p += 16; + // Save follower's colour + memset(name, 0, 16); + strncpy(name, Followercolor_cons_t[players[i].followercolor].strvalue, 16); // Not KartColor_Names because followercolor has extra values such as "Match" + M_Memcpy(demo_p, name, 16); + demo_p += 16; + // Score, since Kart uses this to determine where you start on the map WRITEUINT32(demo_p, player->score); @@ -7425,6 +7431,18 @@ void G_DoPlayDemo(char *defdemoname) demo_p += 16; SetPlayerFollower(p, follower); + // Follower colour + M_Memcpy(color, demo_p, 16); + demo_p += 16; + for (i = 0; i < MAXSKINCOLORS +2; i++) // +2 because of Match and Opposite + { + if (!stricmp(Followercolor_cons_t[i].strvalue, color)) + { + players[p].followercolor = i; + break; + } + } + // Score, since Kart uses this to determine where you start on the map players[p].score = READUINT32(demo_p); @@ -7655,7 +7673,7 @@ void G_AddGhost(char *defdemoname) p += 16; // Follower data was here, skip it, we don't care about it for ghosts. - p += 16; + p += 32; // followerskin (16) + followercolor (16) p += 4; // score p += 2; // powerlevel From 406d7c0b326c85ca29a8d4b28a92e91253cb9025 Mon Sep 17 00:00:00 2001 From: Latapostrophe Date: Mon, 8 Jun 2020 20:37:27 +0200 Subject: [PATCH 37/44] Remove old debug prints --- src/p_saveg.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/p_saveg.c b/src/p_saveg.c index 8b3f468e7..37f6dd80b 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -104,8 +104,6 @@ static void P_NetArchivePlayers(void) UINT16 flags; // size_t q; - CONS_Printf("SENDING NET INFO\n"); - WRITEUINT32(save_p, ARCHIVEBLOCK_PLAYERS); for (i = 0; i < MAXPLAYERS; i++) @@ -308,8 +306,6 @@ static void P_NetUnArchivePlayers(void) INT32 i, j; UINT16 flags; - CONS_Printf("FETCHING NET INFO\n"); - if (READUINT32(save_p) != ARCHIVEBLOCK_PLAYERS) I_Error("Bad $$$.sav at archive block Players"); From 396bd69e84687526a9896b85aab13a65fcb7f28a Mon Sep 17 00:00:00 2001 From: Latapostrophe Date: Tue, 9 Jun 2020 18:10:09 +0200 Subject: [PATCH 38/44] Don't save follower skin in replays if there's no follower... --- src/g_game.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/g_game.c b/src/g_game.c index 074ddf9d4..051689325 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -6487,8 +6487,15 @@ void G_BeginRecording(void) demo_p += 16; // Save follower's skin name + // PS: We must check for 'follower' to determine if the followerskin is valid. It's going to be 0 if we don't have a follower, but 0 is also absolutely a valid follower! + // Doesn't really matter if the follower mobj is valid so long as it exists in a way or another. + memset(name, 0, 16); - strncpy(name, followers[player->followerskin].skinname, 16); + if (player->follower) + strncpy(name, followers[player->followerskin].skinname, 16); + else + strncpy(name, "None", 16); // Say we don't have one, then. + M_Memcpy(demo_p,name,16); demo_p += 16; From 478e9cee7c3594459785b8fdc5ae0507859c03a4 Mon Sep 17 00:00:00 2001 From: Latapostrophe Date: Tue, 9 Jun 2020 19:59:46 +0200 Subject: [PATCH 39/44] Use P_SetTarget --- src/g_game.c | 6 +++++- src/p_mobj.c | 2 +- src/p_user.c | 4 ++-- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/g_game.c b/src/g_game.c index 051689325..285a4c44f 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -2700,6 +2700,9 @@ void G_PlayerReborn(INT32 player) wanted = players[player].kartstuff[k_wanted]; } + // Obliterate follower from existence + P_SetTarget(&players[player].follower, NULL); + memcpy(&respawn, &players[player].respawn, sizeof (respawn)); p = &players[player]; @@ -2762,7 +2765,8 @@ void G_PlayerReborn(INT32 player) p->followerready = followerready; p->followerskin = followerskin; p->followercolor = followercolor; - p->follower = NULL; // respawn a new one with you, it looks better. + //p->follower = NULL; // respawn a new one with you, it looks better. + // ^ Not necessary anyway since it will be respawned regardless considering it doesn't exist anymore. // Don't do anything immediately diff --git a/src/p_mobj.c b/src/p_mobj.c index 37df5bb09..2547f3212 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -11891,7 +11891,7 @@ void P_SpawnPlayer(INT32 playernum) p->awayviewmobj = NULL; p->awayviewtics = 0; - p->follower = NULL; // cleanse follower from existence + P_SetTarget(&p->follower, NULL); // cleanse follower from existence // set the scale to the mobj's destscale so settings get correctly set. if we don't, they sometimes don't. if (cv_kartdebugshrink.value && !modeattacking && !p->bot) diff --git a/src/p_user.c b/src/p_user.c index 1818f21b7..657d45dda 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -8521,7 +8521,7 @@ static void P_HandleFollower(player_t *player) { //CONS_Printf("Spawning follower...\n"); // so let's spawn one! - player->follower = P_SpawnMobj(sx, sy, sz, MT_FOLLOWER); + P_SetTarget(&player->follower, P_SpawnMobj(sx, sy, sz, MT_FOLLOWER)); P_SetFollowerState(player->follower, fl.idlestate); P_SetTarget(&player->follower->target, player->mo); // we need that to know when we need to disappear player->follower->angle = player->mo->angle; @@ -8555,7 +8555,7 @@ static void P_HandleFollower(player_t *player) if (P_MobjWasRemoved(player->follower)) { - player->follower = NULL; // Remove this and respawn one, don't crash the game if Lua decides to P_RemoveMobj this thing. + P_SetTarget(&player->follower, NULL); // Remove this and respawn one, don't crash the game if Lua decides to P_RemoveMobj this thing. return; } From 84a31e288f98fb6b9b4e3480a2a0c1aeb7db1358 Mon Sep 17 00:00:00 2001 From: Latapostrophe Date: Tue, 9 Jun 2020 20:09:18 +0200 Subject: [PATCH 40/44] Fix some V_FLIP stuff in player/follower drawing --- src/m_menu.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/m_menu.c b/src/m_menu.c index 739433ec8..194bb8838 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -7639,7 +7639,7 @@ static void M_StartGrandPrix(INT32 choice) grandprixinfo.gamespeed = KARTSPEED_HARD; grandprixinfo.masterbots = true; break; - + } grandprixinfo.encore = (boolean)(cv_dummygpencore.value); @@ -9552,7 +9552,7 @@ static void M_DrawSetupMultiPlayerMenu(void) sprframe = &sprdef->spriteframes[frame]; patch = W_CachePatchNum(sprframe->lumppat[1], PU_CACHE); - if (sprframe->flip & 1) // Only for first sprite + if (sprframe->flip & 2) // Only for first sprite flags |= V_FLIP; // This sprite is left/right flipped! // draw box around guy @@ -9610,10 +9610,12 @@ static void M_DrawSetupMultiPlayerMenu(void) 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 + if (sprframe->flip & 2) // Only for first sprite flags |= V_FLIP; // This sprite is left/right flipped! - // draw player sprite + // @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) // inverse should never happen { From fbd10d0e6c7d2f7c64fe5db4613e7e52f4a65f8f Mon Sep 17 00:00:00 2001 From: Latapostrophe Date: Wed, 10 Jun 2020 22:52:32 +0200 Subject: [PATCH 41/44] Fix a bruh moment where I deleted follower from relinkpointers when fixing a conflict and it broke every netgame :oh: --- src/p_saveg.c | 7 +++++++ src/r_things.c | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/p_saveg.c b/src/p_saveg.c index 37f6dd80b..16863ae33 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -3180,6 +3180,13 @@ static void P_RelinkPointers(void) if (!P_SetTarget(&mobj->player->awayviewmobj, P_FindNewPosition(temp))) CONS_Debug(DBG_GAMELOGIC, "awayviewmobj not found on %d\n", mobj->type); } + if (mobj->player->follower) + { + temp = (UINT32)(size_t)mobj->player->follower; + mobj->player->follower = NULL; + if (!P_SetTarget(&mobj->player->follower, P_FindNewPosition(temp))) + CONS_Debug(DBG_GAMELOGIC, "follower not found on %d\n", mobj->type); + } if (mobj->player->nextwaypoint) { temp = (UINT32)(size_t)mobj->player->nextwaypoint; diff --git a/src/r_things.c b/src/r_things.c index 369bdfafe..5a428afc8 100644 --- a/src/r_things.c +++ b/src/r_things.c @@ -3099,7 +3099,7 @@ void SetFollower(INT32 playernum, INT32 skinnum) } P_RemoveMobj(player->follower); - player->follower = NULL; + P_SetTarget(&player->follower, NULL); } player->followerskin = skinnum; From 9fec0562c9339a4695dcdcb4c8c7a76437e9fd60 Mon Sep 17 00:00:00 2001 From: Latapostrophe Date: Wed, 10 Jun 2020 23:21:25 +0200 Subject: [PATCH 42/44] Don't do anything if you somehow have a negative follower in menus --- src/m_menu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/m_menu.c b/src/m_menu.c index 194bb8838..a82f97314 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -9638,7 +9638,7 @@ static void M_DrawSetupMultiPlayerMenu(void) static void M_GetFollowerState(void) { - if (setupm_fakefollower == -1 || setupm_fakefollower > numfollowers-1) // yikes, there's none! + 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. From 6d0712628356b01b50dd67e29a16f425ee1e269e Mon Sep 17 00:00:00 2001 From: Latapostrophe Date: Sun, 14 Jun 2020 16:59:46 +0200 Subject: [PATCH 43/44] Use the proper player to save followercolor in replays this time around --- src/g_game.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/g_game.c b/src/g_game.c index 7f6177eac..fbc695f53 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -6497,7 +6497,7 @@ void G_BeginRecording(void) // Save follower's colour memset(name, 0, 16); - strncpy(name, Followercolor_cons_t[players[i].followercolor].strvalue, 16); // Not KartColor_Names because followercolor has extra values such as "Match" + strncpy(name, Followercolor_cons_t[players->followercolor].strvalue, 16); // Not KartColor_Names because followercolor has extra values such as "Match" M_Memcpy(demo_p, name, 16); demo_p += 16; From 01ecc8573a54d0e949518888f5c49bbbf822042a Mon Sep 17 00:00:00 2001 From: Latapostrophe Date: Sun, 14 Jun 2020 17:06:46 +0200 Subject: [PATCH 44/44] Actually use the proper player for followercolor, for real this time --- src/g_game.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/g_game.c b/src/g_game.c index fbc695f53..1b994ede1 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -6497,7 +6497,7 @@ void G_BeginRecording(void) // Save follower's colour memset(name, 0, 16); - strncpy(name, Followercolor_cons_t[players->followercolor].strvalue, 16); // Not KartColor_Names because followercolor has extra values such as "Match" + strncpy(name, Followercolor_cons_t[player->followercolor].strvalue, 16); // Not KartColor_Names because followercolor has extra values such as "Match" M_Memcpy(demo_p, name, 16); demo_p += 16;