From 157e3197f3cecc71225538fa6099f75bf00d9484 Mon Sep 17 00:00:00 2001 From: Latapostrophe Date: Thu, 14 Feb 2019 03:56:28 +0100 Subject: [PATCH] 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 5cf95f4b..0f734f1b 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 c3596f05..9ebd0237 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 eed59d28..7d4aec64 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 4531769a..b72a1cbc 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 ab863c6f..5e7b7657 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 f0d221ff..04d3fe20 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 5701f1c9..ac16a6ed 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 975a4a5d..ae568fab 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 76f57a9b..c2e75916 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 65041c6f..7999db02 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 57ecb0dd..cfb862f0 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