Followers but they crash when you join netgames

This commit is contained in:
Latapostrophe 2019-02-14 03:56:28 +01:00
parent fe3852f005
commit 157e3197f3
13 changed files with 390 additions and 38 deletions

View file

@ -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

View file

@ -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();
}

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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;

View file

@ -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<<FRACBITS, // radius
16<<FRACBITS, // height
1, // display offset
100, // mass
0, // damage
sfx_None, // activesound
MF_NOCLIPTHING|MF_NOGRAVITY|MF_NOCLIP|MF_NOCLIPHEIGHT, // flags
S_NULL // raisestate
},
// ============================================================================================================================//
#ifdef SEENAMES

View file

@ -779,7 +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,
@ -4051,6 +4054,32 @@ typedef enum state
S_KARMAFIREWORK3,
S_KARMAFIREWORK4,
S_KARMAFIREWORKTRAIL,
// followers:
// generic chao:
S_GCHAOIDLE,
S_GCHAOFLY,
S_GCHAOSAD1,
S_GCHAOSAD2,
S_GCHAOSAD3,
S_GCHAOSAD4,
S_GCHAOHAPPY1,
S_GCHAOHAPPY2,
S_GCHAOHAPPY3,
S_GCHAOHAPPY4,
// cheese:
S_CHEESEIDLE,
S_CHEESEFLY,
S_CHEESESAD1,
S_CHEESESAD2,
S_CHEESESAD3,
S_CHEESESAD4,
S_CHEESEHAPPY1,
S_CHEESEHAPPY2,
S_CHEESEHAPPY3,
S_CHEESEHAPPY4,
#ifdef SEENAMES
S_NAMECHECK,
@ -4853,8 +4882,10 @@ typedef enum mobj_type
MT_BOOSTON,
MT_LIZARDMAN,
MT_LIONMAN,
MT_KARMAFIREWORK,
MT_FOLLOWER,
#ifdef SEENAMES
MT_NAMECHECK,

View file

@ -8530,6 +8530,11 @@ static void M_SetupMultiPlayer(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
@ -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

View file

@ -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);

View file

@ -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<<FRACBITS) / 7; // loose approximation, this doesn't need to be incredibly precise
fixed_t sine = 4 * FINESINE((((8*pi*(TICRATE*2)) * leveltime)>>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
{

View file

@ -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;
}

View file

@ -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